From patchwork Tue Nov 24 10:33:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dodji Seketeli X-Patchwork-Id: 41177 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 443863896822; Tue, 24 Nov 2020 10:33:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 443863896822 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1606214037; bh=f2b5S4nlz4wQKBp9hXa8MXiS58ynaJj+rgKbsqv/12s=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Help: List-Subscribe:From:Reply-To:From; b=XFleBdEjLZDJBjtqfQsPHqsjJ4HJZpkwqWuuLToJ4p2CDNGgzm0SutcPM4gdXwGQq smQvjnGk1Rsxr2gjtUZp8WeONNuJuhUoyiyVRmhTvfY+dmZxjyn93/EpW7B3uibKmx Lkv8vHjqw2+/+X13ovEcVO5czl49km13+xDypUoY= X-Original-To: libabigail@sourceware.org Delivered-To: libabigail@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 4F63E3896822 for ; Tue, 24 Nov 2020 10:33:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 4F63E3896822 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-443-gHCFxHmSM1qgKsjWv3w7Rg-1; Tue, 24 Nov 2020 05:33:47 -0500 X-MC-Unique: gHCFxHmSM1qgKsjWv3w7Rg-1 Received: by mail-wm1-f71.google.com with SMTP id g17so1025407wmg.1 for ; Tue, 24 Nov 2020 02:33:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:organization:date:message-id :user-agent:mime-version; bh=f2b5S4nlz4wQKBp9hXa8MXiS58ynaJj+rgKbsqv/12s=; b=NjPOL2Dl1lw/0QrXb1QvuYJXQEVmgpu3Au+IKIGR4wRzr+pjel3ypp4iPbnAl6lGpB +Zk+XppmfNnyhutSgPDW22jFCJXtjwlZjU/wDRMuAsj9uhqqIDnW/Kx3BgSTGnu6Jua0 aFOD4FYFnL5lDINPunEcNrkVZRjPbuJ9RbnOlw/qY+G4oE6KTspi07DDfKIfL1yEfACn A/JBQ9d22LjXhXzrTu+KguvcSj1PcD4r9uuaUlDZWu7JKdShMXLVim+RVMdIPF02n0I2 MMrO7iUFjN698cd5H6J7yfhY+HziPtq3gMmAaxa6RzpwOAD0bRImyt1+4/zBCZ++TLSx ErRQ== X-Gm-Message-State: AOAM533t0HfgkhBLLJQ64knhkL9HUQvClqXJAvGBexw10af/sZS9nSt+ 4gbimu9iVZNGh96WCKdVoZGcus1volEpqyJm5j6oN37Bfit8JrhE+M9CZyvHQI3U63A/G4o14ju P+TjNO3rGr7KMs2CyeDM/w0vj1DEKGUffWLiMIYDMdg40l8tUJCU9EDHy+Y+n6GnDnGeo X-Received: by 2002:a05:6000:45:: with SMTP id k5mr2441542wrx.108.1606214025235; Tue, 24 Nov 2020 02:33:45 -0800 (PST) X-Google-Smtp-Source: ABdhPJyBWub00yVXsWnbc3Tsdu3+1cjNqMciTdGwXTeHlypCCwbZ0Gw+0KFMlQcsA5IZeuBCNqqfsA== X-Received: by 2002:a05:6000:45:: with SMTP id k5mr2441492wrx.108.1606214024635; Tue, 24 Nov 2020 02:33:44 -0800 (PST) Received: from localhost (91-166-131-65.subs.proxad.net. [91.166.131.65]) by smtp.gmail.com with ESMTPSA id 71sm25988944wrm.20.2020.11.24.02.33.43 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 24 Nov 2020 02:33:44 -0800 (PST) Received: by localhost (Postfix, from userid 1001) id C72D91A111B; Tue, 24 Nov 2020 11:33:42 +0100 (CET) To: libabigail@sourceware.org Subject: [PATCH] abipkgdiff: Add a new --self-check option Organization: Red Hat / France X-Operating-System: Red Hat Enterprise Linux Server 7.8 X-URL: http://www.redhat.com Date: Tue, 24 Nov 2020 11:33:42 +0100 Message-ID: <86v9dvqc1l.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libabigail@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list of the Libabigail project List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-Patchwork-Original-From: Dodji Seketeli via Libabigail From: Dodji Seketeli Reply-To: Dodji Seketeli Errors-To: libabigail-bounces@sourceware.org Sender: "Libabigail" Hello, After the report of https://sourceware.org/bugzilla/show_bug.cgi?id=26769 it became apparent that we want to be able compare a binary against its ABIXML representation, including for cases where the binaries are embedded in RPM packages. This patches thus introduces the --self-check option to abipkgdiff so that it can be invoked like this: $ abipkgdiff --self-check --d1 libstdc++-debuginfo-10.2.1-8.fc34.x86_64.rpm --d1 gcc-debuginfo-10.2.1-8.fc34.x86_64.rpm libstdc++-10.2.1-8.fc34.x86_64.rpm ==== SELF CHECK SUCCEEDED for 'libstdc++.so.6.0.28'==== $ With this option, libabigail compares each binary in the RPM against its own ABIXML representation. This should hopefully help to write regression tests which have as sole inputs the links to download the RPMs. It's also useful to ease the process of reproducing the issue raised. This option can now be used, for instance, by the libabigail-selfcheck program over at https://pagure.io/libabigail-selfcheck. * tools/abipkgdiff.cc (options::self_check): Define new data member. (options::options): Initialize it. (display_usage): Add help string for the --self-check option. (parse_command): Parse the new --self-check option. (extract_deb): Add missing newline. (compare): Remove useless white space. (compare_to_self, self_compare_prepared_userspace_package) (self_compare_prepared_package, compare_to_self): Add new static functions. (class self_compare_task): Add new class. (prepare_package): Add a new overload that takes just one parameter. (elf_size_is_greater): Don't crash if the args are empty. (main): If the --self-check option is given, make sure we have just one package in argument. Use the new compare_to_self function to handle the --self-check option. * doc/manuals/abipkgdiff.rst: Add documentation for the new --self-check option. Signed-off-by: Dodji Seketeli --- doc/manuals/abipkgdiff.rst | 17 ++ tools/abipkgdiff.cc | 504 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 505 insertions(+), 16 deletions(-) diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst index 2d8e42d..bc5acb9 100644 --- a/doc/manuals/abipkgdiff.rst +++ b/doc/manuals/abipkgdiff.rst @@ -451,6 +451,23 @@ Options Emit verbose progress messages. + + * ``self-check`` + + This is used to test the underlying Libabigail library. When in + used, the command expects only on input package, along with its + associated debug info packages. The command then compares each + binary inside the package against its own ABIXML + representation. The result of the comparison should yield the + empty set if Libabigail behaves correctly. Otherwise, it means + there is an issue that ought to be fixed. This option is used by + people interested in Libabigail development for regression testing + purposes. Here is an example of the use of this option: :: + + $ abipkgdiff --self-check --d1 mesa-libGLU-debuginfo-9.0.1-3.fc33.x86_64.rpm mesa-libGLU-9.0.1-3.fc33.x86_64.rpm + ==== SELF CHECK SUCCEEDED for 'libGLU.so.1.3.1' ==== + $ + .. _abipkgdiff_return_value_label: Return value diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc index 445e240..344fe28 100644 --- a/tools/abipkgdiff.cc +++ b/tools/abipkgdiff.cc @@ -87,6 +87,7 @@ #endif #include +#include #include #include #include @@ -101,11 +102,14 @@ #include "abg-comparison.h" #include "abg-suppression.h" #include "abg-dwarf-reader.h" +#include "abg-reader.h" +#include "abg-writer.h" using std::cout; using std::cerr; using std::string; using std::ostream; +using std::ofstream; using std::vector; using std::map; using abg_compat::unordered_set; @@ -158,6 +162,13 @@ using abigail::dwarf_reader::create_read_context; using abigail::dwarf_reader::get_soname_of_elf_file; using abigail::dwarf_reader::get_type_of_elf_file; using abigail::dwarf_reader::read_corpus_from_elf; +using abigail::xml_reader::read_corpus_from_native_xml_file; +using abigail::xml_writer::HASH_TYPE_ID_STYLE; +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; + /// The options passed to the current program. class options @@ -203,6 +214,7 @@ public: bool show_added_binaries; bool fail_if_no_debug_info; bool show_identical_binaries; + bool self_check; vector kabi_whitelist_packages; vector suppression_paths; vector kabi_whitelist_paths; @@ -216,8 +228,8 @@ public: nonexistent_file(), abignore(true), parallel(true), - verbose(false), - drop_private_types(false), + verbose(), + drop_private_types(), show_relative_offset_changes(true), no_default_suppression(), keep_tmp_files(), @@ -237,7 +249,8 @@ public: show_symbols_not_referenced_by_debug_info(true), show_added_binaries(true), fail_if_no_debug_info(), - show_identical_binaries() + show_identical_binaries(), + self_check() { // set num_workers to the default number of threads of the // underlying maching. This is the default value for the number @@ -854,6 +867,8 @@ display_usage(const string& prog_name, ostream& out) << " --fail-no-dbg fail if no debug info was found\n" << " --show-identical-binaries show the names of identical binaries\n" << " --verbose emit verbose progress messages\n" + << " --self-check perform a sanity check by comparing " + "binaries inside the input package against their ABIXML representation\n" << " --help|-h display this help message\n" << " --version|-v display program version information" " and exit\n"; @@ -927,7 +942,7 @@ extract_deb(const string& package_path, << package_path << " to " << extracted_package_dir_path - << " ..."; + << " ...\n"; string cmd = "mkdir -p " + extracted_package_dir_path + " && dpkg -x " + package_path + " " + extracted_package_dir_path; @@ -1272,7 +1287,7 @@ compare(const elf_file& elf1, if (opts.verbose) emit_prefix("abipkgdiff", cerr) - << " Reading file " + << "Reading file " << elf1.path << " ...\n"; @@ -1352,13 +1367,13 @@ compare(const elf_file& elf1, if (opts.verbose) emit_prefix("abipkgdiff", cerr) - << " DONE reading file " + << "DONE reading file " << elf1.path << "\n"; if (opts.verbose) emit_prefix("abipkgdiff", cerr) - << " Reading file " + << "Reading file " << elf2.path << " ...\n"; @@ -1466,6 +1481,190 @@ compare(const elf_file& elf1, return s; } +/// Compare an ELF file to its ABIXML representation. +/// +/// @param elf the ELF file to compare. +/// +/// @param debug_dir the debug directory of the ELF file. +/// +/// @param opts the options passed the user. +/// +/// @param env the environment to use for the comparison. +/// +/// @param diff the diff object resulting from the comparison of @p +/// elf against its ABIXML representation. +/// +/// @param ctxt the resulting diff context used for the comparison +/// that yielded @p diff. +/// +/// @param detailed_error_status the detailed error satus returned by +/// this function. +/// +/// @return the status of the self comparison. +static abidiff_status +compare_to_self(const elf_file& elf, + const string& debug_dir, + const options& opts, + abigail::ir::environment_sptr &env, + corpus_diff_sptr &diff, + diff_context_sptr &ctxt, + abigail::dwarf_reader::status *detailed_error_status = 0) +{ + char *di_dir = (char*) debug_dir.c_str(); + + vector di_dirs; + di_dirs.push_back(&di_dir); + + abigail::dwarf_reader::status c_status = abigail::dwarf_reader::STATUS_OK; + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Comparing the ABI of file '" + << elf.path + << "' against itself ...\n"; + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Reading file " + << elf.path + << " ...\n"; + + corpus_sptr corp; + { + read_context_sptr c = + create_read_context(elf.path, di_dirs, env.get(), + /*load_all_types=*/opts.show_all_types); + + corp = read_corpus_from_elf(*c, c_status); + + if (!(c_status & abigail::dwarf_reader::STATUS_OK)) + { + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Could not read file '" + << elf.path + << "' propertly\n"; + + if (detailed_error_status) + *detailed_error_status = c_status; + + return abigail::tools_utils::ABIDIFF_ERROR; + } + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Read file '" + << elf.path + << "' OK\n"; + + + ABG_ASSERT(corp); + } + + corpus_sptr reread_corp; + string abi_file_path; + { + abi_file_path = elf.path + ".abi"; + ofstream of(abi_file_path.c_str(), std::ios_base::trunc); + + + { + const abigail::xml_writer::write_context_sptr c = + abigail::xml_writer::create_write_context(env.get(), of); + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Writting ABIXML file '" + << abi_file_path + << "' ...\n"; + + if (!write_corpus(*c, corp, 0)) + { + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Could not write the ABIXML file to '" + << abi_file_path << "'\n"; + + return abigail::tools_utils::ABIDIFF_ERROR; + } + + of.flush(); + of.close(); + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Wrote ABIXML file '" + << abi_file_path + << "' OK\n"; + } + + { + abigail::xml_reader::read_context_sptr c = + abigail::xml_reader::create_native_xml_read_context(abi_file_path, + env.get()); + if (!c) + { + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Could not create read context for ABIXML file '" + << abi_file_path << "'\n"; + + return abigail::tools_utils::ABIDIFF_ERROR; + } + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Reading ABIXML file '" + << abi_file_path + << "' ...\n"; + + reread_corp = read_corpus_from_input(*c); + if (!reread_corp) + { + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Could not read temporary ABIXML file '" + << abi_file_path << "'\n"; + + return abigail::tools_utils::ABIDIFF_ERROR; + } + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Read file '" + << abi_file_path + << "' OK\n"; + } + } + + ctxt.reset(new diff_context); + set_diff_context_from_opts(ctxt, opts); + + if (opts.verbose) + emit_prefix("abipkgdiff", cerr) + << "Comparing the ABIs of: \n" + << " '" << corp->get_path() << "' against \n" + << " '" << abi_file_path << "'...\n"; + + diff = compute_diff(corp, reread_corp, ctxt); + if (opts.verbose) + emit_prefix("abipkgdfiff", cerr) + << "... Comparing the ABIs: DONE\n"; + + abidiff_status s = abigail::tools_utils::ABIDIFF_OK; + if (diff->has_net_changes()) + s |= abigail::tools_utils::ABIDIFF_ABI_CHANGE; + if (diff->has_incompatible_changes()) + s |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE; + + if (opts.verbose) + emit_prefix("abipkgdfiff", cerr) + << (s == abigail::tools_utils::ABIDIFF_OK) + ? string("Comparison against self SUCCEEDED\n") + : string("Comparison against self FAILED\n"); + return s; +} + /// If devel packages were associated to the main package we are /// looking at, use the names of the header files (extracted from the /// package) to generate suppression specification to filter out types @@ -1834,6 +2033,86 @@ public: /// Convenience typedef for a shared_ptr of @ref compare_task. typedef shared_ptr compare_task_sptr; +/// The worker task which job is to compare an ELF binary to its ABI +/// representation. +class self_compare_task : public compare_task +{ +public: + + compare_args_sptr args; + abidiff_status status; + ostringstream out; + string pretty_output; + + self_compare_task() + : status(abigail::tools_utils::ABIDIFF_OK) + {} + + self_compare_task(const compare_args_sptr& a) + : args(a), + status(abigail::tools_utils::ABIDIFF_OK) + {} + + /// The job performed by the task. + /// + /// This compares an ELF file to its ABIXML representation and + /// expects the result to be the empty set. + virtual void + perform() + { + abigail::ir::environment_sptr env(new abigail::ir::environment); + diff_context_sptr ctxt; + corpus_diff_sptr diff; + + abigail::dwarf_reader::status detailed_status = + abigail::dwarf_reader::STATUS_UNKNOWN; + + status |= compare_to_self(args->elf1, args->debug_dir1, + args->opts, env, diff, ctxt, + &detailed_status); + + string name = args->elf1.name; + if (status == abigail::tools_utils::ABIDIFF_OK) + pretty_output += "==== SELF CHECK SUCCEEDED for '"+ name + "' ====\n"; + else if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE) + ||( diff && diff->has_net_changes())) + { + // There is an ABI change, tell the user about it. + diff->report(out, /*prefix=*/" "); + + pretty_output += + string("======== comparing'") + name + + "' to itself wrongly yielded result: ===========\n" + + out.str() + + "===SELF CHECK FAILED for '"+ name + "'\n"; + } + + // If an error happened while comparing the two binaries, tell the + // user about it. + if (status & abigail::tools_utils::ABIDIFF_ERROR) + { + string diagnostic = + abigail::dwarf_reader::status_to_diagnostic_string(detailed_status); + + if (!diagnostic.empty()) + { + string name = args->elf1.name; + + pretty_output += + "==== Error happened during self check of " + name + ": ====\n"; + + pretty_output += diagnostic; + + pretty_output += + "==== SELF CHECK FAILED for " + name + " ====\n"; + } + } + } +}; // end class self_compare + +/// Convenience typedef for a shared_ptr of @ref compare_task. +typedef shared_ptr self_compare_task_sptr; + /// This function is a sub-routine of create_maps_of_package_content. /// /// It's called during the walking of the directory tree containing @@ -2220,6 +2499,21 @@ prepare_packages(package_sptr &first_package, return first_pkg_prepare->is_ok && second_pkg_prepare->is_ok; } +/// Prepare one package for the sake of comparing it to its ABIXML +/// representation. +/// +/// The preparation entails unpacking the content of the package into +/// a temporary directory and mapping its content. +/// +/// @param pkg the package to prepare. +/// +/// @param opts the options provided by the user. +/// +/// @return true iff the preparation succeeded. +static bool +prepare_package(package_sptr& pkg, options &opts) +{return extract_package_and_map_its_content(pkg, opts);} + /// Compare the added sizes of an ELF pair (specified by a comparison /// task that compares two ELF files) against the added sizes of a /// second ELF pair. @@ -2244,8 +2538,8 @@ elf_size_is_greater(const task_sptr &task1, compare_task_sptr t1 = dynamic_pointer_cast(task1); compare_task_sptr t2 = dynamic_pointer_cast(task2); - off_t s1 = t1->args->elf1.size + t1->args->elf2.size; - off_t s2 = t2->args->elf1.size + t2->args->elf2.size; + off_t s1 = t1->args ? t1->args->elf1.size + t1->args->elf2.size : 0; + off_t s2 = t2->args ? t2->args->elf1.size + t2->args->elf2.size: 0; return s1 > s2; } @@ -2483,6 +2777,112 @@ compare_prepared_userspace_packages(package& first_package, return status; } +/// In the context of the unpacked content of a given package, compare +/// the binaries inside the package against their ABIXML +/// representation. This should yield the empty set. +/// +/// @param pkg (unpacked) package +/// +/// @param diff the representation of the changes between the binaries +/// and their ABIXML. This should obviously be the empty set. +/// +/// @param diff a textual representation of the diff. +/// +/// @param opts the options provided by the user. +static abidiff_status +self_compare_prepared_userspace_package(package& pkg, + abi_diff& diff, + options& opts) +{ + abidiff_status status = abigail::tools_utils::ABIDIFF_OK; + abigail::workers::queue::tasks_type self_compare_tasks; + string pkg_name = pkg.base_name(); + + // Setting debug-info path of libraries + string debug_dir, relative_debug_path = "/usr/lib/debug/"; + if (!pkg.debug_info_packages().empty()) + debug_dir = + pkg.debug_info_packages().front()->extracted_dir_path() + + relative_debug_path; + + suppressions_type supprs; + for (map::iterator it = + pkg.path_elf_file_sptr_map().begin(); + it != pkg.path_elf_file_sptr_map().end(); + ++it) + { + if (it != pkg.path_elf_file_sptr_map().end() + && (it->second->type == abigail::dwarf_reader::ELF_TYPE_DSO + || it->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC + || it->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC + || it->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)) + { + if (it->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE) + { + compare_args_sptr args + (new compare_args(*it->second, + debug_dir, + supprs, + *it->second, + debug_dir, + supprs, + opts)); + self_compare_task_sptr t(new self_compare_task(args)); + self_compare_tasks.push_back(t); + } + } + } + + if (self_compare_tasks.empty()) + { + maybe_erase_temp_dirs(pkg, pkg, opts); + return abigail::tools_utils::ABIDIFF_OK; + } + + // Larger elfs are processed first, since it's usually safe to assume + // their debug-info is larger as well, but the results are still + // in a map ordered by looked up in elf.name order. + std::sort(self_compare_tasks.begin(), + self_compare_tasks.end(), + elf_size_is_greater); + + // There's no reason to spawn more workers than there are ELF pairs + // to be compared. + size_t num_workers = (opts.parallel + ? std::min(opts.num_workers, self_compare_tasks.size()) + : 1); + assert(num_workers >= 1); + + comparison_done_notify notifier(diff); + abigail::workers::queue comparison_queue(num_workers, notifier); + + // Compare all the binaries, in parallel and then wait for the + // comparisons to complete. + comparison_queue.schedule_tasks(self_compare_tasks); + comparison_queue.wait_for_workers_to_complete(); + + // Get the set of comparison tasks that were perform and sort them. + queue::tasks_type& done_tasks = comparison_queue.get_completed_tasks(); + std::sort(done_tasks.begin(), done_tasks.end(), elf_size_is_greater); + + // Print the reports of the comparison to standard output. + for (queue::tasks_type::const_iterator i = done_tasks.begin(); + i != done_tasks.end(); + ++i) + { + self_compare_task_sptr t = dynamic_pointer_cast(*i); + if (t) + cout << t->pretty_output; + } + + // Erase temporary directory tree we might have left behind. + maybe_erase_temp_dirs(pkg, pkg, opts); + + status = notifier.status; + + return status; +} + /// Compare the ABI of two prepared packages that contain linux kernel /// binaries. /// @@ -2626,6 +3026,29 @@ compare_prepared_package(package& first_package, package& second_package, return status; } +/// Compare binaries in a package against their ABIXML +/// representations. +/// +/// @param pkg the package to consider. +/// +/// @param diff the textual representation of the resulting +/// comparison. +/// +/// @param opts the options provided by the user +/// +/// @return the status of the comparison. +static abidiff_status +self_compare_prepared_package(package& pkg, + abi_diff& diff, + options& opts) +{ + abidiff_status status = abigail::tools_utils::ABIDIFF_OK; + + status = self_compare_prepared_userspace_package(pkg, diff, opts); + + return status; +} + /// Compare the ABI of two packages /// /// @param first_package the first package to consider. @@ -2658,6 +3081,24 @@ compare(package_sptr& first_package, package_sptr& second_package, return compare_prepared_package(*first_package, *second_package, diff, opts); } +/// Compare binaries in a package against their ABIXML +/// representations. +/// +/// @param pkg the package to consider. +/// +/// @param opts the options provided by the user +/// +/// @return the status of the comparison. +static abidiff_status +compare_to_self(package_sptr& pkg, options& opts) +{ + if (!prepare_package(pkg, opts)) + return abigail::tools_utils::ABIDIFF_ERROR; + + abi_diff diff; + return self_compare_prepared_package(*pkg, diff, opts); +} + /// Compare the ABI of two packages. /// /// @param first_package the first package to consider. @@ -2830,6 +3271,8 @@ parse_command_line(int argc, char* argv[], options& opts) opts.parallel = false; else if (!strcmp(argv[i], "--show-identical-binaries")) opts.show_identical_binaries = true; + else if (!strcmp(argv[i], "--self-check")) + opts.self_check = true; else if (!strcmp(argv[i], "--suppressions") || !strcmp(argv[i], "--suppr")) { @@ -2976,10 +3419,31 @@ main(int argc, char* argv[]) return (abigail::tools_utils::ABIDIFF_USAGE_ERROR | abigail::tools_utils::ABIDIFF_ERROR); - if (opts.package1.empty() || opts.package2.empty()) + bool need_just_one_input_package = opts.self_check; + + if (need_just_one_input_package) + { + bool bail_out = false; + if (!opts.package2.empty()) + { + // We don't need the second package, we'll ignore it later + // down below. + ; + } + if (opts.package1.empty()) + { + // We need at least one package to work with! + emit_prefix("abipkgdiff", cerr) + << "missing input package\n"; + if (bail_out) + return (abigail::tools_utils::ABIDIFF_USAGE_ERROR + | abigail::tools_utils::ABIDIFF_ERROR); + } + } + else if(opts.package1.empty() || opts.package2.empty()) { emit_prefix("abipkgdiff", cerr) - << "Please enter two packages to compare" << "\n"; + << "please enter two packages to compare" << "\n"; return (abigail::tools_utils::ABIDIFF_USAGE_ERROR | abigail::tools_utils::ABIDIFF_ERROR); } @@ -3036,7 +3500,8 @@ main(int argc, char* argv[]) switch (first_package->type()) { case abigail::tools_utils::FILE_TYPE_RPM: - if (second_package->type() != abigail::tools_utils::FILE_TYPE_RPM) + if (!second_package->path().empty() + && second_package->type() != abigail::tools_utils::FILE_TYPE_RPM) { base_name(opts.package2, package_name); emit_prefix("abipkgdiff", cerr) @@ -3063,7 +3528,8 @@ main(int argc, char* argv[]) } if (first_package->debug_info_packages().empty() - || second_package->debug_info_packages().empty()) + || (!second_package->path().empty() + && second_package->debug_info_packages().empty())) { emit_prefix("abipkgdiff", cerr) << "a Linux Kernel package must be accompanied with its " @@ -3086,7 +3552,8 @@ main(int argc, char* argv[]) break; case abigail::tools_utils::FILE_TYPE_DEB: - if (second_package->type() != abigail::tools_utils::FILE_TYPE_DEB) + if (!second_package->path().empty() + && second_package->type() != abigail::tools_utils::FILE_TYPE_DEB) { base_name(opts.package2, package_name); emit_prefix("abipkgdiff", cerr) @@ -3097,7 +3564,8 @@ main(int argc, char* argv[]) break; case abigail::tools_utils::FILE_TYPE_DIR: - if (second_package->type() != abigail::tools_utils::FILE_TYPE_DIR) + if (!second_package->path().empty() + && second_package->type() != abigail::tools_utils::FILE_TYPE_DIR) { base_name(opts.package2, package_name); emit_prefix("abipkgdiff", cerr) @@ -3108,7 +3576,8 @@ main(int argc, char* argv[]) break; case abigail::tools_utils::FILE_TYPE_TAR: - if (second_package->type() != abigail::tools_utils::FILE_TYPE_TAR) + if (!second_package->path().empty() + && second_package->type() != abigail::tools_utils::FILE_TYPE_TAR) { base_name(opts.package2, package_name); emit_prefix("abipkgdiff", cerr) @@ -3126,5 +3595,8 @@ main(int argc, char* argv[]) | abigail::tools_utils::ABIDIFF_ERROR); } + if (opts.self_check) + return compare_to_self(first_package, opts); + return compare(first_package, second_package, opts); }