diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-28 21:12:31 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-28 21:12:31 +0000 |
commit | 4d05b61640a3668740029b6b660c136975690ca3 (patch) | |
tree | 816a39318cf25a492bac3026fab8d5ad5d2a9ea5 | |
parent | c83ae6994732498fd9161cc8f009dbf64f4bde37 (diff) | |
parent | eb4bb7c513362eea24833fd9989ef6b7c6dccd98 (diff) | |
download | stg-simpleperf-release.tar.gz |
Snap for 11510257 from eb4bb7c513362eea24833fd9989ef6b7c6dccd98 to simpleperf-releasesimpleperf-release
Change-Id: I797e552e747b1f1708d278793755eddcb245950d
41 files changed, 980 insertions, 424 deletions
@@ -61,6 +61,8 @@ cc_defaults { enabled: false, }, }, + // TODO(b/324274771): figure out a better solution + native_coverage: false, } cc_library_host_static { @@ -81,12 +83,12 @@ cc_library_host_static { "fingerprint.cc", "graph.cc", "input.cc", - "metrics.cc", "naming.cc", "post_processing.cc", "proto_reader.cc", "proto_writer.cc", "reporting.cc", + "runtime.cc", "stable_hash.cc", "stg.proto", "type_normalisation.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index bb112da..9587c6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,12 +92,12 @@ add_library(libstg OBJECT fingerprint.cc graph.cc input.cc - metrics.cc naming.cc post_processing.cc proto_reader.cc proto_writer.cc reporting.cc + runtime.cc stable_hash.cc type_normalisation.cc type_resolution.cc @@ -139,9 +139,9 @@ else() error_test file_descriptor_test filter_test - metrics_test order_test reporting_test + runtime_test scc_test stgdiff_test ) diff --git a/abigail_reader.cc b/abigail_reader.cc index 2682c49..f1deff3 100644 --- a/abigail_reader.cc +++ b/abigail_reader.cc @@ -28,7 +28,6 @@ #include <cstddef> #include <cstdint> #include <functional> -#include <iomanip> #include <ios> #include <map> #include <memory> @@ -47,7 +46,7 @@ #include "error.h" #include "file_descriptor.h" #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "scope.h" #include "type_normalisation.h" @@ -88,7 +87,7 @@ xmlNodePtr Next(xmlNodePtr node) { } xmlNodePtr GetOnlyChild(xmlNodePtr element) { - xmlNodePtr child = Child(element); + const xmlNodePtr child = Child(element); if (child == nullptr || Next(child) != nullptr) { Die() << "element '" << GetName(element) << "' without exactly one child"; } @@ -119,7 +118,7 @@ std::string GetAttributeOrDie(xmlNodePtr node, const char* name) { } // Set an attribute value. -void SetAttribute(xmlNodePtr node, const char* name, const std::string &value) { +void SetAttribute(xmlNodePtr node, const char* name, const std::string& value) { xmlSetProp(node, ToLibxml(name), ToLibxml(value.c_str())); } @@ -275,7 +274,7 @@ void StripNonElements(xmlNodePtr node) { case XML_ELEMENT_NODE: { xmlNodePtr child = Child(node); while (child) { - xmlNodePtr next = Next(child); + const xmlNodePtr next = Next(child); StripNonElements(child); child = next; } @@ -1148,7 +1147,8 @@ void Abigail::ProcessStructUnion(Id id, bool is_struct, } void Abigail::ProcessEnum(Id id, xmlNodePtr enumeration) { - bool forward = ReadAttribute<bool>(enumeration, "is-declaration-only", false); + const bool forward = + ReadAttribute<bool>(enumeration, "is-declaration-only", false); const auto name = ReadAttribute<bool>(enumeration, "is-anonymous", false) ? std::string() : scope_name_ + GetAttributeOrDie(enumeration, "name"); @@ -1157,8 +1157,8 @@ void Abigail::ProcessEnum(Id id, xmlNodePtr enumeration) { return; } - xmlNodePtr underlying = Child(enumeration); - Check(underlying) << "enum-decl has no child elements"; + const xmlNodePtr underlying = Child(enumeration); + Check(underlying != nullptr) << "enum-decl has no child elements"; CheckName("underlying-type", underlying); const auto type = GetEdge(underlying); @@ -1188,16 +1188,17 @@ Id Abigail::ProcessBaseClass(xmlNodePtr base_class) { std::optional<Id> Abigail::ProcessDataMember(bool is_struct, xmlNodePtr data_member) { - xmlNodePtr decl = GetOnlyChild(data_member); + const xmlNodePtr decl = GetOnlyChild(data_member); CheckName("var-decl", decl); if (ReadAttribute<bool>(data_member, "static", false)) { ProcessDecl(true, decl); return {}; } - size_t offset = is_struct - ? ReadAttributeOrDie<size_t>(data_member, "layout-offset-in-bits") - : 0; + const auto offset = is_struct + ? ReadAttributeOrDie<size_t>(data_member, + "layout-offset-in-bits") + : 0; const auto name = GetAttributeOrDie(decl, "name"); const auto type = GetEdge(decl); @@ -1207,7 +1208,7 @@ std::optional<Id> Abigail::ProcessDataMember(bool is_struct, void Abigail::ProcessMemberFunction(std::vector<Id>& methods, xmlNodePtr method) { - xmlNodePtr decl = GetOnlyChild(method); + const xmlNodePtr decl = GetOnlyChild(method); CheckName("function-decl", decl); // ProcessDecl creates symbol references so must be called unconditionally. const auto type = ProcessDecl(false, decl); @@ -1222,7 +1223,7 @@ void Abigail::ProcessMemberFunction(std::vector<Id>& methods, } void Abigail::ProcessMemberType(xmlNodePtr member_type) { - xmlNodePtr decl = GetOnlyChild(member_type); + const xmlNodePtr decl = GetOnlyChild(member_type); const auto type_id = GetAttributeOrDie(decl, "id"); const auto id = GetNode(type_id); if (graph_.Is(id)) { @@ -1284,15 +1285,15 @@ Id Abigail::BuildSymbols() { return graph_.Add<Interface>(symbols); } -Document Read(const std::string& path, Metrics& metrics) { +Document Read(Runtime& runtime, const std::string& path) { // Open input for reading. - FileDescriptor fd(path.c_str(), O_RDONLY); + const FileDescriptor fd(path.c_str(), O_RDONLY); // Read the XML. Document document(nullptr, xmlFreeDoc); { - Time t(metrics, "abigail.libxml_parse"); - std::unique_ptr< + const Time t(runtime, "abigail.libxml_parse"); + const std::unique_ptr< std::remove_pointer_t<xmlParserCtxtPtr>, void(*)(xmlParserCtxtPtr)> context(xmlNewParserCtxt(), xmlFreeParserCtxt); document.reset( @@ -1304,10 +1305,10 @@ Document Read(const std::string& path, Metrics& metrics) { return document; } -Id Read(Graph& graph, const std::string& path, Metrics& metrics) { - const Document document = Read(path, metrics); - xmlNodePtr root = xmlDocGetRootElement(document.get()); - Check(root) << "XML document has no root element"; +Id Read(Runtime& runtime, Graph& graph, const std::string& path) { + const Document document = Read(runtime, path); + const xmlNodePtr root = xmlDocGetRootElement(document.get()); + Check(root != nullptr) << "XML document has no root element"; return Abigail(graph).ProcessRoot(root); } diff --git a/abigail_reader.h b/abigail_reader.h index 21ada8d..3123f93 100644 --- a/abigail_reader.h +++ b/abigail_reader.h @@ -32,7 +32,7 @@ #include <libxml/tree.h> #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "scope.h" namespace stg { @@ -146,7 +146,7 @@ class Abigail { Id BuildSymbols(); }; -Id Read(Graph& graph, const std::string& path, Metrics& metrics); +Id Read(Runtime& runtime, Graph& graph, const std::string& path); // Exposed for testing. void Clean(xmlNodePtr root); @@ -154,7 +154,7 @@ bool EqualTree(xmlNodePtr left, xmlNodePtr right); bool SubTree(xmlNodePtr left, xmlNodePtr right); using Document = std::unique_ptr<std::remove_pointer_t<xmlDocPtr>, void(*)(xmlDocPtr)>; -Document Read(const std::string& path, Metrics& metrics); +Document Read(Runtime& runtime, const std::string& path); } // namespace abixml } // namespace stg diff --git a/abigail_reader_test.cc b/abigail_reader_test.cc index 2396b77..25c5121 100644 --- a/abigail_reader_test.cc +++ b/abigail_reader_test.cc @@ -28,9 +28,9 @@ #include <catch2/catch.hpp> #include "abigail_reader.h" -#include "graph.h" -#include "metrics.h" #include "equality.h" +#include "graph.h" +#include "runtime.h" namespace { @@ -39,13 +39,13 @@ std::filesystem::path filename_to_path(const char* f) { } stg::abixml::Document Read(const char* input) { - stg::Metrics metrics; - return stg::abixml::Read(filename_to_path(input), metrics); + stg::Runtime runtime(std::cerr, false); + return stg::abixml::Read(runtime, filename_to_path(input)); } stg::Id Read(stg::Graph& graph, const char* input) { - stg::Metrics metrics; - return stg::abixml::Read(graph, filename_to_path(input), metrics); + stg::Runtime runtime(std::cerr, false); + return stg::abixml::Read(runtime, graph, filename_to_path(input)); } struct EqualTreeTestCase { @@ -199,7 +199,11 @@ TEST_CASE("Tidy") { TidyTestCase( {"duplicate type resolution - stray anonymous member", {"abigail_duplicate_types_7.xml", - "abigail_duplicate_types_8.xml"}})); + "abigail_duplicate_types_8.xml"}}), + TidyTestCase( + {"corpus group handling", + {"abigail_duplicate_types_0.xml", + "abigail_duplicate_types_9.xml"}})); SECTION(test.name) { // Read inputs. diff --git a/btf_reader.cc b/btf_reader.cc index a228e31..75ec360 100644 --- a/btf_reader.cc +++ b/btf_reader.cc @@ -41,8 +41,8 @@ #include <linux/btf.h> #include "elf_loader.h" #include "error.h" -#include "graph.h" #include "file_descriptor.h" +#include "graph.h" #include "reader_options.h" namespace stg { diff --git a/comparison.cc b/comparison.cc index 0115d05..ca821f9 100644 --- a/comparison.cc +++ b/comparison.cc @@ -428,6 +428,13 @@ Result Compare::operator()(const BaseClass& x1, const BaseClass& x2) { return result; } +Result Compare::operator()(const Method& x1, const Method& x2) { + Result result; + result.MaybeAddNodeDiff("vtable offset", x1.vtable_offset, x2.vtable_offset); + result.MaybeAddEdgeDiff("", (*this)(x1.type_id, x2.type_id)); + return result; +} + Result Compare::operator()(const Member& x1, const Member& x2) { Result result; result.MaybeAddNodeDiff("offset", x1.offset, x2.offset); @@ -447,13 +454,6 @@ Result Compare::operator()(const Member& x1, const Member& x2) { return result; } -Result Compare::operator()(const Method& x1, const Method& x2) { - Result result; - result.MaybeAddNodeDiff("vtable offset", x1.vtable_offset, x2.vtable_offset); - result.MaybeAddEdgeDiff("", (*this)(x1.type_id, x2.type_id)); - return result; -} - Result Compare::operator()(const StructUnion& x1, const StructUnion& x2) { Result result; // Compare two anonymous types recursively, not holding diffs. @@ -740,6 +740,10 @@ std::string MatchingKey::operator()(const BaseClass& x) { return (*this)(x.type_id); } +std::string MatchingKey::operator()(const Method& x) { + return x.name + ',' + x.mangled_name; +} + std::string MatchingKey::operator()(const Member& x) { if (!x.name.empty()) { return x.name; @@ -747,10 +751,6 @@ std::string MatchingKey::operator()(const Member& x) { return (*this)(x.type_id); } -std::string MatchingKey::operator()(const Method& x) { - return x.name + ',' + x.mangled_name; -} - std::string MatchingKey::operator()(const StructUnion& x) { if (!x.name.empty()) { return x.name; diff --git a/comparison.h b/comparison.h index de2c562..693c55a 100644 --- a/comparison.h +++ b/comparison.h @@ -38,7 +38,7 @@ #include <vector> #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "scc.h" namespace stg { @@ -212,8 +212,8 @@ struct MatchingKey { explicit MatchingKey(const Graph& graph) : graph(graph) {} std::string operator()(Id id); std::string operator()(const BaseClass&); - std::string operator()(const Member&); std::string operator()(const Method&); + std::string operator()(const Member&); std::string operator()(const StructUnion&); template <typename Node> std::string operator()(const Node&); @@ -258,15 +258,15 @@ struct ResolveQualifier { }; struct Compare { - Compare(const Graph& graph, const Ignore& ignore, Metrics& metrics) + Compare(Runtime& runtime, const Graph& graph, const Ignore& ignore) : graph(graph), ignore(ignore), - queried(metrics, "compare.queried"), - already_compared(metrics, "compare.already_compared"), - being_compared(metrics, "compare.being_compared"), - really_compared(metrics, "compare.really_compared"), - equivalent(metrics, "compare.equivalent"), - inequivalent(metrics, "compare.inequivalent"), - scc_size(metrics, "compare.scc_size") {} + queried(runtime, "compare.queried"), + already_compared(runtime, "compare.already_compared"), + being_compared(runtime, "compare.being_compared"), + really_compared(runtime, "compare.really_compared"), + equivalent(runtime, "compare.equivalent"), + inequivalent(runtime, "compare.inequivalent"), + scc_size(runtime, "compare.scc_size") {} std::pair<bool, std::optional<Comparison>> operator()(Id id1, Id id2); Comparison Removed(Id id); @@ -282,8 +282,8 @@ struct Compare { Result operator()(const Primitive&, const Primitive&); Result operator()(const Array&, const Array&); Result operator()(const BaseClass&, const BaseClass&); - Result operator()(const Member&, const Member&); Result operator()(const Method&, const Method&); + Result operator()(const Member&, const Member&); Result operator()(const StructUnion&, const StructUnion&); Result operator()(const Enumeration&, const Enumeration&); Result operator()(const Function&, const Function&); diff --git a/deduplication.cc b/deduplication.cc index a3b1f9d..b71c599 100644 --- a/deduplication.cc +++ b/deduplication.cc @@ -21,31 +21,33 @@ #include <cstddef> #include <unordered_map> +#include <utility> #include <vector> #include "equality.h" #include "equality_cache.h" #include "graph.h" -#include "metrics.h" +#include "hashing.h" +#include "runtime.h" #include "substitution.h" namespace stg { -Id Deduplicate(Graph& graph, Id root, const Hashes& hashes, Metrics& metrics) { +Id Deduplicate(Runtime& runtime, Graph& graph, Id root, const Hashes& hashes) { // Partition the nodes by hash. std::unordered_map<HashValue, std::vector<Id>> partitions; { - Time x(metrics, "partition nodes"); + const Time x(runtime, "partition nodes"); for (const auto& [id, fp] : hashes) { partitions[fp].push_back(id); } } - Counter(metrics, "deduplicate.nodes") = hashes.size(); - Counter(metrics, "deduplicate.hashes") = partitions.size(); + Counter(runtime, "deduplicate.nodes") = hashes.size(); + Counter(runtime, "deduplicate.hashes") = partitions.size(); - Histogram hash_partition_size(metrics, "deduplicate.hash_partition_size"); - Counter min_comparisons(metrics, "deduplicate.min_comparisons"); - Counter max_comparisons(metrics, "deduplicate.max_comparisons"); + Histogram hash_partition_size(runtime, "deduplicate.hash_partition_size"); + Counter min_comparisons(runtime, "deduplicate.min_comparisons"); + Counter max_comparisons(runtime, "deduplicate.max_comparisons"); for (const auto& [fp, ids] : partitions) { const auto n = ids.size(); hash_partition_size.Add(n); @@ -54,16 +56,16 @@ Id Deduplicate(Graph& graph, Id root, const Hashes& hashes, Metrics& metrics) { } // Refine partitions of nodes with the same fingerprints. - EqualityCache cache(hashes, metrics); + EqualityCache cache(runtime, hashes); Equals<EqualityCache> equals(graph, cache); - Counter equalities(metrics, "deduplicate.equalities"); - Counter inequalities(metrics, "deduplicate.inequalities"); + Counter equalities(runtime, "deduplicate.equalities"); + Counter inequalities(runtime, "deduplicate.inequalities"); { - Time x(metrics, "find duplicates"); + const Time x(runtime, "find duplicates"); for (auto& [fp, ids] : partitions) { while (ids.size() > 1) { std::vector<Id> todo; - Id candidate = ids[0]; + const Id candidate = ids[0]; for (size_t i = 1; i < ids.size(); ++i) { if (equals(ids[i], candidate)) { ++equalities; @@ -78,8 +80,8 @@ Id Deduplicate(Graph& graph, Id root, const Hashes& hashes, Metrics& metrics) { } // Keep one representative of each set of duplicates. - Counter unique(metrics, "deduplicate.unique"); - Counter duplicate(metrics, "deduplicate.duplicate"); + Counter unique(runtime, "deduplicate.unique"); + Counter duplicate(runtime, "deduplicate.duplicate"); auto remap = [&cache](Id& id) { // update id to representative id, avoiding silent stores const Id fid = cache.Find(id); @@ -89,9 +91,9 @@ Id Deduplicate(Graph& graph, Id root, const Hashes& hashes, Metrics& metrics) { }; Substitute substitute(graph, remap); { - Time x(metrics, "rewrite"); + const Time x(runtime, "rewrite"); for (const auto& [id, fp] : hashes) { - Id fid = cache.Find(id); + const Id fid = cache.Find(id); if (fid != id) { graph.Remove(id); ++duplicate; diff --git a/deduplication.h b/deduplication.h index 73e0dea..adb8c89 100644 --- a/deduplication.h +++ b/deduplication.h @@ -25,13 +25,13 @@ #include "graph.h" #include "hashing.h" -#include "metrics.h" +#include "runtime.h" namespace stg { using Hashes = std::unordered_map<Id, HashValue>; -Id Deduplicate(Graph& graph, Id root, const Hashes& hashes, Metrics& metrics); +Id Deduplicate(Runtime& runtime, Graph& graph, Id root, const Hashes& hashes); } // namespace stg diff --git a/dwarf_processor.cc b/dwarf_processor.cc index 48d6e26..99278d8 100644 --- a/dwarf_processor.cc +++ b/dwarf_processor.cc @@ -96,7 +96,7 @@ size_t GetByteSize(Entry& entry) { } Primitive::Encoding GetEncoding(Entry& entry) { - auto dwarf_encoding = entry.MaybeGetUnsignedConstant(DW_AT_encoding); + const auto dwarf_encoding = entry.MaybeGetUnsignedConstant(DW_AT_encoding); if (!dwarf_encoding) { Die() << "Encoding was not found for " << EntryToString(entry); } @@ -313,7 +313,7 @@ class Processor { void ProcessInternal(Entry& entry) { ++result_.processed_entries; - auto tag = entry.GetTag(); + const auto tag = entry.GetTag(); switch (tag) { case DW_TAG_array_type: ProcessArray(entry); @@ -414,7 +414,7 @@ class Processor { } void ProcessNamespace(Entry& entry) { - auto name = GetNameOrEmpty(entry); + const auto name = GetNameOrEmpty(entry); const PushScopeName push_scope_name(scope_, "namespace", name); ProcessAllChildren(entry); } @@ -432,9 +432,10 @@ class Processor { } void ProcessTypedef(Entry& entry) { - const std::string type_name = scope_ + GetName(entry); - auto referred_type_id = GetIdForReferredType(MaybeGetReferredType(entry)); - const Id id = AddProcessedNode<Typedef>(entry, type_name, referred_type_id); + const auto type_name = GetName(entry); + const auto full_name = scope_ + type_name; + const Id referred_type_id = GetReferredTypeId(MaybeGetReferredType(entry)); + const Id id = AddProcessedNode<Typedef>(entry, full_name, referred_type_id); if (!ShouldKeepDefinition(entry, type_name)) { // We always model (and keep) typedef definitions. But we should exclude // filtered out types from being type roots. @@ -445,15 +446,14 @@ class Processor { template<typename Node, typename KindType> void ProcessReference(Entry& entry, KindType kind) { - auto referred_type_id = GetIdForReferredType(MaybeGetReferredType(entry)); + const Id referred_type_id = GetReferredTypeId(MaybeGetReferredType(entry)); AddProcessedNode<Node>(entry, kind, referred_type_id); } void ProcessPointerToMember(Entry& entry) { const Id containing_type_id = - GetIdForReferredType(entry.MaybeGetReference(DW_AT_containing_type)); - const Id pointee_type_id = - GetIdForReferredType(MaybeGetReferredType(entry)); + GetReferredTypeId(entry.MaybeGetReference(DW_AT_containing_type)); + const Id pointee_type_id = GetReferredTypeId(MaybeGetReferredType(entry)); AddProcessedNode<PointerToMember>(entry, containing_type_id, pointee_type_id); } @@ -477,16 +477,16 @@ class Processor { if (name.substr(0, kBuiltinPrefix.size()) == kBuiltinPrefix) { return true; } - Die() << "File filter is provided, but DWARF entry << " - << EntryToString(entry) << " << doesn't have DW_AT_decl_file"; + Die() << "File filter is provided, but " << name << " (" + << EntryToString(entry) << ") doesn't have DW_AT_decl_file"; } return (*file_filter_)(*file); } void ProcessStructUnion(Entry& entry, StructUnion::Kind kind) { - std::string name = GetNameOrEmpty(entry); - const std::string full_name = name.empty() ? std::string() : scope_ + name; - const PushScopeName push_scope_name(scope_, kind, name); + const auto type_name = GetNameOrEmpty(entry); + const auto full_name = type_name.empty() ? type_name : scope_ + type_name; + const PushScopeName push_scope_name(scope_, kind, type_name); std::vector<Id> base_classes; std::vector<Id> members; @@ -544,6 +544,9 @@ class Processor { // We just skip these as neither GCC nor Clang seem to use them // properly (resulting in no references to such DIEs). break; + case DW_TAG_variant_part: + // TODO: Add a DWARF processor to process variants. + break; default: Die() << "Unexpected tag for child of struct/class/union: " << Hex(child_tag) << ", " << EntryToString(child); @@ -551,7 +554,7 @@ class Processor { } if (entry.GetFlag(DW_AT_declaration) || - !ShouldKeepDefinition(entry, name)) { + !ShouldKeepDefinition(entry, type_name)) { // Declaration may have partial information about members or method. // We only need to parse children for information that will be needed in // complete definition, but don't need to store them in incomplete node. @@ -570,9 +573,9 @@ class Processor { } void ProcessMember(Entry& entry) { - std::string name = GetNameOrEmpty(entry); + const auto name = GetNameOrEmpty(entry); auto referred_type = GetReferredType(entry); - auto referred_type_id = GetIdForEntry(referred_type); + const Id referred_type_id = GetIdForEntry(referred_type); auto optional_bit_size = entry.MaybeGetUnsignedConstant(DW_AT_bit_size); // Member has DW_AT_bit_size if and only if it is bit field. // STG uses bit_size == 0 to mark that the member is not a bit field. @@ -618,7 +621,7 @@ class Processor { } void ProcessBaseClass(Entry& entry) { - const auto type_id = GetIdForReferredType(GetReferredType(entry)); + const Id type_id = GetReferredTypeId(GetReferredType(entry)); const auto byte_offset = entry.MaybeGetMemberByteOffset(); if (!byte_offset) { Die() << "No offset found for base class " << EntryToString(entry); @@ -639,7 +642,7 @@ class Processor { void ProcessArray(Entry& entry) { auto referred_type = GetReferredType(entry); - auto referred_type_id = GetIdForEntry(referred_type); + Id referred_type_id = GetIdForEntry(referred_type); auto children = entry.GetChildren(); // Multiple children in array describe multiple dimensions of this array. // For example, int[M][N] contains two children, M located in the first @@ -663,43 +666,57 @@ class Processor { } void ProcessEnum(Entry& entry) { - const std::optional<std::string> name_optional = MaybeGetName(entry); - const std::string name = - name_optional.has_value() ? scope_ + *name_optional : ""; + const auto type_name = GetNameOrEmpty(entry); + const auto full_name = type_name.empty() ? type_name : scope_ + type_name; if (entry.GetFlag(DW_AT_declaration)) { // It is expected to have only name and no children in declaration. // However, it is not guaranteed and we should do something if we find an // example. CheckNoChildren(entry); - AddProcessedNode<Enumeration>(entry, name); + AddProcessedNode<Enumeration>(entry, full_name); return; } - auto underlying_type_id = GetIdForReferredType(MaybeGetReferredType(entry)); + const Id underlying_type_id = + GetReferredTypeId(MaybeGetReferredType(entry)); auto children = entry.GetChildren(); Enumeration::Enumerators enumerators; enumerators.reserve(children.size()); for (auto& child : children) { - Check(child.GetTag() == DW_TAG_enumerator) - << "Enum expects child of DW_TAG_enumerator"; - std::string enumerator_name = GetName(child); - // TODO: detect signedness of underlying type and call - // an appropriate method. - std::optional<size_t> value_optional = - child.MaybeGetUnsignedConstant(DW_AT_const_value); - Check(value_optional.has_value()) << "Enumerator should have value"; - // TODO: support both uint64_t and int64_t, depending on - // signedness of underlying type. - enumerators.emplace_back(enumerator_name, - static_cast<int64_t>(*value_optional)); - } - if (!ShouldKeepDefinition(entry, name)) { - AddProcessedNode<Enumeration>(entry, name); + auto child_tag = child.GetTag(); + switch (child_tag) { + case DW_TAG_enumerator: { + const std::string enumerator_name = GetName(child); + // TODO: detect signedness of underlying type and call + // an appropriate method. + std::optional<size_t> value_optional = + child.MaybeGetUnsignedConstant(DW_AT_const_value); + Check(value_optional.has_value()) << "Enumerator should have value"; + // TODO: support both uint64_t and int64_t, depending on + // signedness of underlying type. + enumerators.emplace_back(enumerator_name, + static_cast<int64_t>(*value_optional)); + break; + } + case DW_TAG_subprogram: + // STG does not support virtual methods for enums. + Check(child.MaybeGetUnsignedConstant(DW_AT_virtuality) + .value_or(DW_VIRTUALITY_none) == DW_VIRTUALITY_none) + << "Enums can not have virtual methods: " << EntryToString(child); + ProcessFunction(child); + break; + default: + Die() << "Unexpected tag for child of enum: " << Hex(child_tag) + << ", " << EntryToString(child); + } + } + if (!ShouldKeepDefinition(entry, type_name)) { + AddProcessedNode<Enumeration>(entry, full_name); return; } - const Id id = AddProcessedNode<Enumeration>(entry, name, underlying_type_id, - std::move(enumerators)); - if (!name.empty()) { + const Id id = AddProcessedNode<Enumeration>( + entry, full_name, underlying_type_id, std::move(enumerators)); + if (!full_name.empty()) { AddNamedTypeNode(id); } } @@ -787,7 +804,7 @@ class Processor { auto name_with_context = GetNameWithContext(entry); auto referred_type = GetReferredType(entry); - auto referred_type_id = GetIdForEntry(referred_type); + const Id referred_type_id = GetIdForEntry(referred_type); if (auto address = entry.MaybeGetAddress(DW_AT_location)) { // Only external variables with address are useful for ABI monitoring @@ -824,14 +841,14 @@ class Processor { }; Subprogram GetSubprogram(Entry& entry) { - auto return_type_id = GetIdForReferredType(MaybeGetReferredType(entry)); + const Id return_type_id = GetReferredTypeId(MaybeGetReferredType(entry)); std::vector<Id> parameters; for (auto& child : entry.GetChildren()) { auto child_tag = child.GetTag(); switch (child_tag) { case DW_TAG_formal_parameter: - parameters.push_back(GetIdForReferredType(GetReferredType(child))); + parameters.push_back(GetReferredTypeId(GetReferredType(child))); break; case DW_TAG_unspecified_parameters: // Note: C++ allows a single ... argument specification but C does @@ -908,19 +925,19 @@ class Processor { // Same as GetIdForEntry, but returns "void_id_" for "unspecified" references, // because it is normal for DWARF (5.2 Unspecified Type Entries). - Id GetIdForReferredType(std::optional<Entry> referred_type) { + Id GetReferredTypeId(std::optional<Entry> referred_type) { return referred_type ? GetIdForEntry(*referred_type) : void_id_; } // Wrapper for GetIdForEntry to allow lvalues. - Id GetIdForReferredType(Entry referred_type) { + Id GetReferredTypeId(Entry referred_type) { return GetIdForEntry(referred_type); } // Populate Id from method above with processed Node. template <typename Node, typename... Args> Id AddProcessedNode(Entry& entry, Args&&... args) { - auto id = GetIdForEntry(entry); + const Id id = GetIdForEntry(entry); graph_.Set<Node>(id, std::forward<Args>(args)...); return id; } diff --git a/elf_reader.cc b/elf_reader.cc index 2913007..31d39cb 100644 --- a/elf_reader.cc +++ b/elf_reader.cc @@ -35,8 +35,8 @@ #include "error.h" #include "filter.h" #include "graph.h" -#include "metrics.h" #include "reader_options.h" +#include "runtime.h" #include "type_normalisation.h" #include "type_resolution.h" #include "unification.h" @@ -191,23 +191,23 @@ namespace { class Reader { public: - Reader(Graph& graph, const std::string& path, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics) + Reader(Runtime& runtime, Graph& graph, const std::string& path, + ReadOptions options, const std::unique_ptr<Filter>& file_filter) : graph_(graph), dwarf_(path), elf_(dwarf_.GetElf()), options_(options), file_filter_(file_filter), - metrics_(metrics) {} + runtime_(runtime) {} - Reader(Graph& graph, char* data, size_t size, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics) + Reader(Runtime& runtime, Graph& graph, char* data, size_t size, + ReadOptions options, const std::unique_ptr<Filter>& file_filter) : graph_(graph), dwarf_(data, size), elf_(dwarf_.GetElf()), options_(options), file_filter_(file_filter), - metrics_(metrics) {} + runtime_(runtime) {} Id Read(); @@ -223,7 +223,7 @@ class Reader { // the nodes in consideration to the ones allocated by the DWARF processor // here and any symbol or type roots that follow. This is done by setting // the starting node ID to be the current graph limit. - Unification unification(graph_, graph_.Limit(), metrics_); + Unification unification(runtime_, graph_, graph_.Limit()); const dwarf::Types types = dwarf::Process( dwarf_, elf_.IsLittleEndianBinary(), file_filter_, graph_); @@ -289,7 +289,7 @@ class Reader { } roots.push_back(root); - stg::ResolveTypes(graph_, unification, {roots}, metrics_); + stg::ResolveTypes(runtime_, graph_, unification, {roots}); unification.Update(root); return root; @@ -392,7 +392,7 @@ class Reader { elf::ElfLoader elf_; ReadOptions options_; const std::unique_ptr<Filter>& file_filter_; - Metrics& metrics_; + Runtime& runtime_; }; Id Reader::Read() { @@ -437,14 +437,14 @@ Id Reader::Read() { } // namespace } // namespace internal -Id Read(Graph& graph, const std::string& path, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics) { - return internal::Reader(graph, path, options, file_filter, metrics).Read(); +Id Read(Runtime& runtime, Graph& graph, const std::string& path, + ReadOptions options, const std::unique_ptr<Filter>& file_filter) { + return internal::Reader(runtime, graph, path, options, file_filter).Read(); } -Id Read(Graph& graph, char* data, size_t size, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics) { - return internal::Reader(graph, data, size, options, file_filter, metrics) +Id Read(Runtime& runtime, Graph& graph, char* data, size_t size, + ReadOptions options, const std::unique_ptr<Filter>& file_filter) { + return internal::Reader(runtime, graph, data, size, options, file_filter) .Read(); } diff --git a/elf_reader.h b/elf_reader.h index 12d5711..159096a 100644 --- a/elf_reader.h +++ b/elf_reader.h @@ -31,16 +31,16 @@ #include "elf_loader.h" #include "filter.h" #include "graph.h" -#include "metrics.h" #include "reader_options.h" +#include "runtime.h" namespace stg { namespace elf { -Id Read(Graph& graph, const std::string& path, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics); -Id Read(Graph& graph, char* data, size_t size, ReadOptions options, - const std::unique_ptr<Filter>& file_filter, Metrics& metrics); +Id Read(Runtime& runtime, Graph& graph, const std::string& path, + ReadOptions options, const std::unique_ptr<Filter>& file_filter); +Id Read(Runtime& runtime, Graph& graph, char* data, size_t size, + ReadOptions options, const std::unique_ptr<Filter>& file_filter); // For unit tests only namespace internal { @@ -58,7 +58,7 @@ struct Equals { } // Comparison opened, need to close it before returning. - bool result = graph.Apply2<bool>(*this, id1, id2); + const auto result = graph.Apply2<bool>(*this, id1, id2); // Check for a complete Strongly-Connected Component. auto comparisons = scc.Close(*handle); diff --git a/equality_cache.h b/equality_cache.h index 0b15b39..9373aa2 100644 --- a/equality_cache.h +++ b/equality_cache.h @@ -29,7 +29,7 @@ #include "graph.h" #include "hashing.h" -#include "metrics.h" +#include "runtime.h" namespace stg { @@ -46,25 +46,25 @@ namespace stg { // Node hashes such as those generated by the Fingerprint function object may be // supplied to avoid equality testing when hashes differ. struct EqualityCache { - EqualityCache(const std::unordered_map<Id, HashValue>& hashes, - Metrics& metrics) + EqualityCache(Runtime& runtime, + const std::unordered_map<Id, HashValue>& hashes) : hashes(hashes), - query_count(metrics, "cache.query_count"), - query_equal_ids(metrics, "cache.query_equal_ids"), - query_unequal_hashes(metrics, "cache.query_unequal_hashes"), - query_equal_representatives(metrics, + query_count(runtime, "cache.query_count"), + query_equal_ids(runtime, "cache.query_equal_ids"), + query_unequal_hashes(runtime, "cache.query_unequal_hashes"), + query_equal_representatives(runtime, "cache.query_equal_representatives"), - query_inequality_found(metrics, "cache.query_inequality_found"), - query_not_found(metrics, "cache.query_not_found"), - find_halved(metrics, "cache.find_halved"), - union_known(metrics, "cache.union_known"), - union_rank_swap(metrics, "cache.union_rank_swap"), - union_rank_increase(metrics, "cache.union_rank_increase"), - union_rank_zero(metrics, "cache.union_rank_zero"), - union_unknown(metrics, "cache.union_unknown"), - disunion_known_hash(metrics, "cache.disunion_known_hash"), - disunion_known_inequality(metrics, "cache.disunion_known_inequality"), - disunion_unknown(metrics, "cache.disunion_unknown") {} + query_inequality_found(runtime, "cache.query_inequality_found"), + query_not_found(runtime, "cache.query_not_found"), + find_halved(runtime, "cache.find_halved"), + union_known(runtime, "cache.union_known"), + union_rank_swap(runtime, "cache.union_rank_swap"), + union_rank_increase(runtime, "cache.union_rank_increase"), + union_rank_zero(runtime, "cache.union_rank_zero"), + union_unknown(runtime, "cache.union_unknown"), + disunion_known_hash(runtime, "cache.disunion_known_hash"), + disunion_known_inequality(runtime, "cache.disunion_known_inequality"), + disunion_unknown(runtime, "cache.disunion_unknown") {} std::optional<bool> Query(const Pair& comparison) { ++query_count; @@ -226,11 +226,11 @@ struct EqualityCache { }; struct SimpleEqualityCache { - explicit SimpleEqualityCache(Metrics& metrics) - : query_count(metrics, "simple_cache.query_count"), - query_equal_ids(metrics, "simple_cache.query_equal_ids"), - query_known_equality(metrics, "simple_cache.query_known_equality"), - known_equality_inserts(metrics, "simple_cache.known_equality_inserts") { + explicit SimpleEqualityCache(Runtime& runtime) + : query_count(runtime, "simple_cache.query_count"), + query_equal_ids(runtime, "simple_cache.query_equal_ids"), + query_known_equality(runtime, "simple_cache.query_known_equality"), + known_equality_inserts(runtime, "simple_cache.known_equality_inserts") { } std::optional<bool> Query(const Pair& comparison) { @@ -124,7 +124,7 @@ template <typename T> std::ostream& operator<<(std::ostream& os, const Hex<T>& hex_value) { // not quite right if an exception is thrown const auto flags = os.flags(); - os << std::hex << std::showbase << hex_value.value; + os << "0x" << std::hex << hex_value.value; os.flags(flags); return os; } diff --git a/fingerprint.cc b/fingerprint.cc index 5d87677..b97b2ff 100644 --- a/fingerprint.cc +++ b/fingerprint.cc @@ -19,6 +19,7 @@ #include "fingerprint.h" +#include <cstdint> #include <map> #include <string> #include <unordered_map> @@ -28,17 +29,18 @@ #include "graph.h" #include "hashing.h" -#include "metrics.h" +#include "runtime.h" #include "scc.h" namespace stg { namespace { struct Hasher { - Hasher(const Graph& graph, std::unordered_map<Id, HashValue>& hashes, - std::unordered_set<Id>& todo, Metrics& metrics) + Hasher(Runtime& runtime, const Graph& graph, + std::unordered_map<Id, HashValue>& hashes, + std::unordered_set<Id>& todo) : graph(graph), hashes(hashes), todo(todo), - non_trivial_scc_size(metrics, "fingerprint.non_trivial_scc_size") {} + non_trivial_scc_size(runtime, "fingerprint.non_trivial_scc_size") {} // Graph function implementation HashValue operator()(const Special& x) { @@ -94,7 +96,7 @@ struct Hasher { auto h = hash('U', static_cast<uint32_t>(x.kind), x.name); if (x.definition.has_value()) { h = hash(h, '1'); - auto& definition = *x.definition; + const auto& definition = *x.definition; ToDo(definition.base_classes); ToDo(definition.methods); if (x.name.empty()) { @@ -166,7 +168,7 @@ struct Hasher { } // Comparison opened, need to close it before returning. - HashValue result = graph.Apply<HashValue>(*this, id); + auto result = graph.Apply<HashValue>(*this, id); // Check for a complete Strongly-Connected Component. auto ids = scc.Close(*handle); @@ -218,11 +220,11 @@ struct Hasher { } // namespace std::unordered_map<Id, HashValue> Fingerprint( - const Graph& graph, Id root, Metrics& metrics) { - Time x(metrics, "hash nodes"); + Runtime& runtime, const Graph& graph, Id root) { + const Time x(runtime, "hash nodes"); std::unordered_map<Id, HashValue> hashes; std::unordered_set<Id> todo; - Hasher hasher(graph, hashes, todo, metrics); + Hasher hasher(runtime, graph, hashes, todo); todo.insert(root); while (!todo.empty()) { for (auto id : std::exchange(todo, {})) { diff --git a/fingerprint.h b/fingerprint.h index f5f8042..43fe694 100644 --- a/fingerprint.h +++ b/fingerprint.h @@ -25,7 +25,7 @@ #include "graph.h" #include "hashing.h" -#include "metrics.h" +#include "runtime.h" namespace stg { @@ -35,7 +35,7 @@ namespace stg { // Given any mutual dependencies between hashes, it falls back to a very poor // but safe hash for the affected nodes: the size of the SCC. std::unordered_map<Id, HashValue> Fingerprint( - const Graph& graph, Id root, Metrics& metrics); + Runtime& runtime, const Graph& graph, Id root); } // namespace stg diff --git a/fuzz/elf_reader_fuzzer.cc b/fuzz/elf_reader_fuzzer.cc index e2d8f55..d83b012 100644 --- a/fuzz/elf_reader_fuzzer.cc +++ b/fuzz/elf_reader_fuzzer.cc @@ -18,24 +18,26 @@ // Author: Matthias Maennich // Author: Aleksei Vetrov +#include <sstream> #include <vector> #include "elf_reader.h" #include "error.h" #include "graph.h" -#include "metrics.h" #include "reader_options.h" +#include "runtime.h" extern "C" int LLVMFuzzerTestOneInput(char* data, size_t size) { try { // Fuzzer forbids changing "data", but libdwfl, used in elf::Read, requires // read and write access to memory. // Luckily, such trivial copy can be easily tracked by fuzzer. - std::vector<char> data_copy(data, data + size); + std::ostringstream os; + stg::Runtime runtime(os, false); stg::Graph graph; - stg::Metrics metrics; - stg::elf::Read(graph, data_copy.data(), size, stg::ReadOptions(), nullptr, - metrics); + std::vector<char> data_copy(data, data + size); + stg::elf::Read(runtime, graph, data_copy.data(), size, stg::ReadOptions(), + nullptr); } catch (const stg::Exception&) { // Pass as this is us catching invalid ELF properly. } @@ -28,32 +28,32 @@ #include "error.h" #include "filter.h" #include "graph.h" -#include "metrics.h" #include "proto_reader.h" #include "reader_options.h" +#include "runtime.h" namespace stg { namespace { -Id ReadInternal(Graph& graph, InputFormat format, const char* input, - ReadOptions options, const std::unique_ptr<Filter>& file_filter, - Metrics& metrics) { +Id ReadInternal(Runtime& runtime, Graph& graph, InputFormat format, + const char* input, ReadOptions options, + const std::unique_ptr<Filter>& file_filter) { switch (format) { case InputFormat::ABI: { - Time read(metrics, "read ABI"); - return abixml::Read(graph, input, metrics); + const Time read(runtime, "read ABI"); + return abixml::Read(runtime, graph, input); } case InputFormat::BTF: { - Time read(metrics, "read BTF"); + const Time read(runtime, "read BTF"); return btf::ReadFile(graph, input, options); } case InputFormat::ELF: { - Time read(metrics, "read ELF"); - return elf::Read(graph, input, options, file_filter, metrics); + const Time read(runtime, "read ELF"); + return elf::Read(runtime, graph, input, options, file_filter); } case InputFormat::STG: { - Time read(metrics, "read STG"); + const Time read(runtime, "read STG"); return proto::Read(graph, input); } } @@ -61,11 +61,10 @@ Id ReadInternal(Graph& graph, InputFormat format, const char* input, } // namespace -Id Read(Graph& graph, InputFormat format, const char* input, - ReadOptions options, const std::unique_ptr<Filter>& file_filter, - Metrics& metrics) { +Id Read(Runtime& runtime, Graph& graph, InputFormat format, const char* input, + ReadOptions options, const std::unique_ptr<Filter>& file_filter) { try { - return ReadInternal(graph, format, input, options, file_filter, metrics); + return ReadInternal(runtime, graph, format, input, options, file_filter); } catch (Exception& e) { std::ostringstream os; os << "processing file '" << input << '\''; @@ -24,16 +24,15 @@ #include "filter.h" #include "graph.h" -#include "metrics.h" #include "reader_options.h" +#include "runtime.h" namespace stg { enum class InputFormat { ABI, BTF, ELF, STG }; -Id Read(Graph& graph, InputFormat format, const char* input, - ReadOptions options, const std::unique_ptr<Filter>& file_filter, - Metrics& metrics); +Id Read(Runtime& runtime, Graph& graph, InputFormat format, const char* input, + ReadOptions options, const std::unique_ptr<Filter>& file_filter); } // namespace stg @@ -173,6 +173,13 @@ Name Describe::operator()(const BaseClass& x) { return (*this)(x.type_id); } +Name Describe::operator()(const Method& x) { + if (x.mangled_name == x.name) { + return Name{x.name}; + } + return Name{x.name + " {" + x.mangled_name + "}"}; +} + Name Describe::operator()(const Member& x) { auto description = (*this)(x.type_id); if (!x.name.empty()) { @@ -185,13 +192,6 @@ Name Describe::operator()(const Member& x) { return description; } -Name Describe::operator()(const Method& x) { - if (x.mangled_name == x.name) { - return Name{x.name}; - } - return Name{x.name + " {" + x.mangled_name + "}"}; -} - Name Describe::operator()(const StructUnion& x) { std::ostringstream os; os << x.kind << ' '; @@ -258,14 +258,14 @@ std::string DescribeKind::operator()(const BaseClass&) { return "base class"; } -std::string DescribeKind::operator()(const Member&) { - return "member"; -} - std::string DescribeKind::operator()(const Method&) { return "method"; } +std::string DescribeKind::operator()(const Member&) { + return "member"; +} + std::string DescribeKind::operator()(const ElfSymbol& x) { std::ostringstream os; os << x.symbol_type << " symbol"; @@ -66,8 +66,8 @@ struct Describe { Name operator()(const Primitive&); Name operator()(const Array&); Name operator()(const BaseClass&); - Name operator()(const Member&); Name operator()(const Method&); + Name operator()(const Member&); Name operator()(const StructUnion&); Name operator()(const Enumeration&); Name operator()(const Function&); @@ -81,8 +81,8 @@ struct DescribeKind { explicit DescribeKind(const Graph& graph) : graph(graph) {} std::string operator()(Id id); std::string operator()(const BaseClass&); - std::string operator()(const Member&); std::string operator()(const Method&); + std::string operator()(const Member&); std::string operator()(const ElfSymbol&); std::string operator()(const Interface&); template <typename Node> diff --git a/proto_writer.cc b/proto_writer.cc index 122872a..d3de03d 100644 --- a/proto_writer.cc +++ b/proto_writer.cc @@ -497,8 +497,7 @@ class HexPrinter : public google::protobuf::TextFormat::FastFieldValuePrinter { google::protobuf::TextFormat::BaseTextGenerator* generator) const override { std::ostringstream os; // 0x01234567 - os << std::showbase << std::hex << std::setfill('0') << std::internal - << std::setw(10) << value; + os << "0x" << std::hex << std::setfill('0') << std::setw(8) << value; generator->PrintString(os.str()); } }; @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- mode: C++ -*- // -// Copyright 2021-2022 Google LLC +// Copyright 2021-2023 Google LLC // // Licensed under the Apache License v2.0 with LLVM Exceptions (the // "License"); you may not use this file except in compliance with the @@ -17,27 +17,18 @@ // // Author: Giuliano Procida -#include "metrics.h" +#include "runtime.h" #include <cstddef> #include <iomanip> #include <map> #include <ostream> -#include <utility> -#include <variant> namespace stg { -namespace { - -std::ostream& operator<<(std::ostream& os, std::monostate) { - return os << "<incomplete>"; -} - -std::ostream& operator<<(std::ostream& os, - const std::map<size_t, size_t>& frequencies) { +std::ostream& operator<<(std::ostream& os, const Frequencies& frequencies) { bool separate = false; - for (const auto& [item, frequency] : frequencies) { + for (const auto& [item, frequency] : frequencies.counts) { if (separate) { os << ' '; } else { @@ -56,20 +47,9 @@ std::ostream& operator<<(std::ostream& os, const Nanoseconds& value) { << std::setfill(' ') << " ms"; } -} // namespace - -void Report(const Metrics& metrics, std::ostream& os) { - for (const auto& metric : metrics) { - std::visit([&](auto&& value) { - os << metric.name << ": " << value << '\n'; - }, metric.value); - } -} - -Time::Time(Metrics& metrics, const char* name) - : metrics_(metrics), index_(metrics.size()) { +Time::Time(Runtime& runtime, const char* name) + : runtime_(runtime), name_(name) { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_); - metrics_.push_back(Metric{name, std::monostate()}); } Time::~Time() { @@ -77,25 +57,24 @@ Time::~Time() { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &finish); const auto seconds = finish.tv_sec - start_.tv_sec; const auto nanos = finish.tv_nsec - start_.tv_nsec; - metrics_[index_].value.emplace<1>(seconds * 1'000'000'000 + nanos); + const Nanoseconds value(seconds * 1'000'000'000 + nanos); + runtime_.PrintMetric(name_, value); } -Counter::Counter(Metrics& metrics, const char* name) - : metrics_(metrics), index_(metrics.size()), value_(0) { - metrics_.push_back(Metric{name, std::monostate()}); +Counter::Counter(Runtime& runtime, const char* name) + : runtime_(runtime), name_(name), value_(0) { } Counter::~Counter() { - metrics_[index_].value = value_; + runtime_.PrintMetric(name_, value_); } -Histogram::Histogram(Metrics& metrics, const char* name) - : metrics_(metrics), index_(metrics.size()) { - metrics_.push_back(Metric{name, std::monostate()}); +Histogram::Histogram(Runtime& runtime, const char* name) + : runtime_(runtime), name_(name) { } Histogram::~Histogram() { - metrics_[index_].value.emplace<3>(std::move(frequencies_)); + runtime_.PrintMetric(name_, frequencies_); } } // namespace stg @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- mode: C++ -*- // -// Copyright 2021-2022 Google LLC +// Copyright 2021-2023 Google LLC // // Licensed under the Apache License v2.0 with LLVM Exceptions (the // "License"); you may not use this file except in compliance with the @@ -17,16 +17,14 @@ // // Author: Giuliano Procida -#ifndef STG_METRICS_H_ -#define STG_METRICS_H_ +#ifndef STG_RUNTIME_H_ +#define STG_RUNTIME_H_ #include <cstddef> #include <cstdint> #include <ctime> #include <map> #include <ostream> -#include <variant> -#include <vector> namespace stg { @@ -35,36 +33,43 @@ struct Nanoseconds { uint64_t ns; }; -struct Metric { - const char* name; - std::variant< - std::monostate, - Nanoseconds, - size_t, - std::map<size_t, size_t> - > value; +struct Frequencies { + std::map<size_t, size_t> counts; }; -using Metrics = std::vector<Metric>; - -void Report(const Metrics& metrics, std::ostream& os); +struct Runtime { + Runtime(std::ostream& output, bool print_metrics) + : output(output), print_metrics(print_metrics) {} + template <typename V> + void PrintMetric(const char* name, const V& value) { + if (print_metrics) { + output << name << ": " << value << '\n'; + } + } + std::ostream& output; + bool print_metrics; +}; // These objects only record values on destruction, so scope them! class Time { public: - Time(Metrics& metrics, const char* name); + Time(Runtime& runtime, const char* name); + Time(Time&& other) = delete; + Time& operator=(Time&& other) = delete; ~Time(); private: - Metrics& metrics_; - size_t index_; + Runtime& runtime_; + const char* name_; struct timespec start_; }; class Counter { public: - Counter(Metrics& metrics, const char* name); + Counter(Runtime& runtime, const char* name); + Counter(Counter&& other) = delete; + Counter& operator=(Counter&& other) = delete; ~Counter(); Counter& operator=(size_t x) { @@ -83,26 +88,28 @@ class Counter { } private: - Metrics& metrics_; - size_t index_; + Runtime& runtime_; + const char* name_; size_t value_; }; class Histogram { public: - Histogram(Metrics& metrics, const char* name); + Histogram(Runtime& runtime, const char* name); + Histogram(Histogram&& other) = delete; + Histogram& operator=(Histogram&& other) = delete; ~Histogram(); void Add(size_t item) { - ++frequencies_[item]; + ++frequencies_.counts[item]; } private: - Metrics& metrics_; - size_t index_; - std::map<size_t, size_t> frequencies_; + Runtime& runtime_; + const char* name_; + Frequencies frequencies_; }; } // namespace stg -#endif // STG_METRICS_H_ +#endif // STG_RUNTIME_H_ diff --git a/metrics_test.cc b/runtime_test.cc index 71b5929..78075af 100644 --- a/metrics_test.cc +++ b/runtime_test.cc @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- mode: C++ -*- // -// Copyright 2022 Google LLC +// Copyright 2022-2023 Google LLC // // Licensed under the Apache License v2.0 with LLVM Exceptions (the // "License"); you may not use this file except in compliance with the @@ -17,49 +17,53 @@ // // Author: Giuliano Procida -#include "metrics.h" +#include "runtime.h" +#include <array> #include <cstddef> -#include <cstdint> #include <sstream> #include <string> -#include <vector> #include <catch2/catch.hpp> namespace Test { TEST_CASE("empty") { - stg::Metrics metrics; std::ostringstream os; - stg::Report(metrics, os); + { + const stg::Runtime runtime(os, true); + } CHECK(os.str().empty()); } -TEST_CASE("incomplete") { - stg::Metrics metrics; - std::ostringstream os; - stg::Time a(metrics, "a"); - stg::Counter b(metrics, "b"); - stg::Histogram c(metrics, "c"); - stg::Report(metrics, os); - const std::string expected = - "a: <incomplete>\nb: <incomplete>\nc: <incomplete>\n"; - CHECK(os.str() == expected); -} - TEST_CASE("times") { - stg::Metrics metrics; const size_t count = 20; - std::vector<stg::Time> times; - for (size_t i = 0; i < count; ++i) { - times.emplace_back(metrics, "name"); - } - for (size_t i = 0; i < count; ++i) { - times.pop_back(); - } std::ostringstream os; - stg::Report(metrics, os); + { + stg::Runtime runtime(os, true); + const std::array<const stg::Time, count> timers = { + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + stg::Time(runtime, "name"), + }; + } std::istringstream is(os.str()); const std::string name = "name:"; const std::string ms = "ms"; @@ -71,9 +75,7 @@ TEST_CASE("times") { std::string second; is >> first >> time >> second; CHECK(first == name); - if (last_time != 0.0) { - CHECK(time < last_time); - } + CHECK(time > last_time); CHECK(second == ms); last_time = time; ++index; @@ -86,13 +88,14 @@ TEST_CASE("times") { } TEST_CASE("counters") { - stg::Metrics metrics; + std::ostringstream os; { - stg::Counter a(metrics, "a"); - stg::Counter b(metrics, "b"); - stg::Counter c(metrics, "c"); - stg::Counter d(metrics, "d"); - stg::Counter e(metrics, "e"); + stg::Runtime runtime(os, true); + stg::Counter a(runtime, "a"); + stg::Counter b(runtime, "b"); + stg::Counter c(runtime, "c"); + const stg::Counter d(runtime, "d"); + stg::Counter e(runtime, "e"); c = 17; ++b; ++b; @@ -100,23 +103,20 @@ TEST_CASE("counters") { a = 3; c += 2; } - std::ostringstream os; - Report(metrics, os); - const std::string expected = "a: 3\nb: 2\nc: 19\nd: 0\ne: 1\n"; + const std::string expected = "e: 1\nd: 0\nc: 19\nb: 2\na: 3\n"; CHECK(os.str() == expected); } TEST_CASE("histogram") { - stg::Metrics metrics; + std::ostringstream os; { - stg::Histogram h(metrics, "h"); + stg::Runtime runtime(os, true); + stg::Histogram h(runtime, "h"); h.Add(13); h.Add(14); h.Add(13); h.Add(12); } - std::ostringstream os; - Report(metrics, os); const std::string expected = "h: [12]=1 [13]=2 [14]=1\n"; CHECK(os.str() == expected); } @@ -35,9 +35,9 @@ #include "fingerprint.h" #include "graph.h" #include "input.h" -#include "metrics.h" #include "proto_writer.h" #include "reader_options.h" +#include "runtime.h" #include "type_resolution.h" #include "unification.h" @@ -55,10 +55,10 @@ struct GetInterface { } }; -Id Merge(Graph& graph, const std::vector<Id>& roots, Metrics& metrics) { +Id Merge(Runtime& runtime, Graph& graph, const std::vector<Id>& roots) { bool failed = false; // this rewrites the graph on destruction - Unification unification(graph, Id(0), metrics); + Unification unification(runtime, graph, Id(0)); unification.Reserve(graph.Limit()); std::map<std::string, Id> symbols; std::map<std::string, Id> types; @@ -99,10 +99,10 @@ void FilterSymbols(Graph& graph, Id root, const Filter& filter) { std::swap(interface.symbols, symbols); } -void Write(const Graph& graph, Id root, const char* output, Metrics& metrics) { +void Write(Runtime& runtime, const Graph& graph, Id root, const char* output) { std::ofstream os(output); { - Time x(metrics, "write"); + const Time x(runtime, "write"); proto::Writer writer(graph); writer.Write(root, os); os << std::flush; @@ -204,33 +204,30 @@ int main(int argc, char* argv[]) { try { stg::Graph graph; - stg::Metrics metrics; + stg::Runtime runtime(std::cerr, opt_metrics); std::vector<stg::Id> roots; roots.reserve(inputs.size()); for (auto& [format, input] : inputs) { - roots.push_back(stg::Read(graph, format, input, opt_read_options, - opt_file_filter, metrics)); + roots.push_back(stg::Read(runtime, graph, format, input, opt_read_options, + opt_file_filter)); } stg::Id root = - roots.size() == 1 ? roots[0] : stg::Merge(graph, roots, metrics); + roots.size() == 1 ? roots[0] : stg::Merge(runtime, graph, roots); if (opt_symbol_filter) { stg::FilterSymbols(graph, root, *opt_symbol_filter); } if (!opt_keep_duplicates) { { - stg::Unification unification(graph, stg::Id(0), metrics); + stg::Unification unification(runtime, graph, stg::Id(0)); unification.Reserve(graph.Limit()); - stg::ResolveTypes(graph, unification, {root}, metrics); + stg::ResolveTypes(runtime, graph, unification, {root}); unification.Update(root); } - const auto hashes = stg::Fingerprint(graph, root, metrics); - root = stg::Deduplicate(graph, root, hashes, metrics); + const auto hashes = stg::Fingerprint(runtime, graph, root); + root = stg::Deduplicate(runtime, graph, root, hashes); } for (auto output : outputs) { - stg::Write(graph, root, output, metrics); - } - if (opt_metrics) { - stg::Report(metrics, std::cerr); + stg::Write(runtime, graph, root, output); } return 0; } catch (const stg::Exception& e) { @@ -120,7 +120,7 @@ message Primitive { fixed32 id = 1; string name = 2; optional Encoding encoding = 3; - fixed32 bytesize = 4; + uint32 bytesize = 4; } message Array { @@ -31,14 +31,16 @@ #include <utility> #include <vector> +#include "comparison.h" #include "equality.h" #include "error.h" #include "fidelity.h" #include "graph.h" #include "input.h" -#include "metrics.h" +#include "naming.h" #include "reader_options.h" #include "reporting.h" +#include "runtime.h" namespace { @@ -50,12 +52,12 @@ using Inputs = std::vector<std::pair<stg::InputFormat, const char*>>; using Outputs = std::vector<std::pair<stg::reporting::OutputFormat, const char*>>; -std::vector<stg::Id> Read(const Inputs& inputs, stg::Graph& graph, - stg::ReadOptions options, stg::Metrics& metrics) { +std::vector<stg::Id> Read(stg::Runtime& runtime, const Inputs& inputs, + stg::Graph& graph, stg::ReadOptions options) { std::vector<stg::Id> roots; for (const auto& [format, filename] : inputs) { - roots.push_back(stg::Read(graph, format, filename, options, nullptr, - metrics)); + roots.push_back(stg::Read(runtime, graph, format, filename, options, + nullptr)); } return roots; } @@ -74,10 +76,10 @@ int RunFidelity(const char* filename, const stg::Graph& graph, return diffs_reported ? kFidelityChange : 0; } -int RunExact(const Inputs& inputs, stg::ReadOptions options, - stg::Metrics& metrics) { +int RunExact(stg::Runtime& runtime, const Inputs& inputs, + stg::ReadOptions options) { stg::Graph graph; - const auto roots = Read(inputs, graph, options, metrics); + const auto roots = Read(runtime, inputs, graph, options); struct PairCache { std::optional<bool> Query(const stg::Pair& comparison) const { @@ -94,25 +96,25 @@ int RunExact(const Inputs& inputs, stg::ReadOptions options, std::unordered_set<stg::Pair> equalities; }; - stg::Time compute(metrics, "equality check"); + const stg::Time compute(runtime, "equality check"); PairCache equalities; return stg::Equals<PairCache>(graph, equalities)(roots[0], roots[1]) ? 0 : kAbiChange; } -int Run(const Inputs& inputs, const Outputs& outputs, stg::Ignore ignore, - stg::ReadOptions options, std::optional<const char*> fidelity, - stg::Metrics& metrics) { +int Run(stg::Runtime& runtime, const Inputs& inputs, const Outputs& outputs, + stg::Ignore ignore, stg::ReadOptions options, + std::optional<const char*> fidelity) { // Read inputs. stg::Graph graph; - const auto roots = Read(inputs, graph, options, metrics); + const auto roots = Read(runtime, inputs, graph, options); // Compute differences. - stg::Compare compare{graph, ignore, metrics}; + stg::Compare compare{runtime, graph, ignore}; std::pair<bool, std::optional<stg::Comparison>> result; { - stg::Time compute(metrics, "compute diffs"); + const stg::Time compute(runtime, "compute diffs"); result = compare(roots[0], roots[1]); } stg::Check(compare.scc.Empty()) << "internal error: SCC state broken"; @@ -124,10 +126,10 @@ int Run(const Inputs& inputs, const Outputs& outputs, stg::Ignore ignore, for (const auto& [format, filename] : outputs) { std::ofstream output(filename); if (comparison) { - stg::Time report(metrics, "report diffs"); - stg::reporting::Options options{format, kMaxCrcOnlyChanges}; - stg::reporting::Reporting reporting{graph, compare.outcomes, options, - names}; + const stg::Time report(runtime, "report diffs"); + const stg::reporting::Options options{format, kMaxCrcOnlyChanges}; + const stg::reporting::Reporting reporting{graph, compare.outcomes, + options, names}; Report(reporting, *comparison, output); output << std::flush; } @@ -138,7 +140,7 @@ int Run(const Inputs& inputs, const Outputs& outputs, stg::Ignore ignore, // Compute fidelity diff if requested. if (fidelity) { - const stg::Time report(metrics, "fidelity"); + const stg::Time report(runtime, "fidelity"); status |= RunFidelity(*fidelity, graph, roots); } @@ -261,14 +263,10 @@ int main(int argc, char* argv[]) { } try { - stg::Metrics metrics; - const int status = opt_exact ? RunExact(inputs, opt_read_options, metrics) - : Run(inputs, outputs, opt_ignore, - opt_read_options, opt_fidelity, metrics); - if (opt_metrics) { - stg::Report(metrics, std::cerr); - } - return status; + stg::Runtime runtime(std::cerr, opt_metrics); + return opt_exact ? RunExact(runtime, inputs, opt_read_options) + : Run(runtime, inputs, outputs, opt_ignore, + opt_read_options, opt_fidelity); } catch (const stg::Exception& e) { std::cerr << e.what(); return 1; diff --git a/stgdiff_test.cc b/stgdiff_test.cc index a09a868..306ddc3 100644 --- a/stgdiff_test.cc +++ b/stgdiff_test.cc @@ -19,8 +19,6 @@ #include <filesystem> #include <fstream> -#include <iostream> -#include <ostream> #include <sstream> #include <string> @@ -28,9 +26,9 @@ #include "comparison.h" #include "graph.h" #include "input.h" -#include "metrics.h" #include "reader_options.h" #include "reporting.h" +#include "runtime.h" namespace { @@ -49,10 +47,10 @@ std::string filename_to_path(const std::string& f) { return std::filesystem::path("testdata") / f; } -stg::Id Read(stg::Graph& graph, stg::InputFormat format, - const std::string& input, stg::Metrics& metrics) { - return stg::Read(graph, format, filename_to_path(input).c_str(), - stg::ReadOptions(), nullptr, metrics); +stg::Id Read(stg::Runtime& runtime, stg::Graph& graph, stg::InputFormat format, + const std::string& input) { + return stg::Read(runtime, graph, format, filename_to_path(input).c_str(), + stg::ReadOptions(), nullptr); } TEST_CASE("ignore") { @@ -240,15 +238,16 @@ TEST_CASE("ignore") { ); SECTION(test.name) { - stg::Metrics metrics; + std::ostringstream os; + stg::Runtime runtime(os, false); // Read inputs. stg::Graph graph; - const auto id0 = Read(graph, test.format0, test.file0, metrics); - const auto id1 = Read(graph, test.format1, test.file1, metrics); + const auto id0 = Read(runtime, graph, test.format0, test.file0); + const auto id1 = Read(runtime, graph, test.format1, test.file1); // Compute differences. - stg::Compare compare{graph, test.ignore, metrics}; + stg::Compare compare{runtime, graph, test.ignore}; const auto& [equals, comparison] = compare(id0, id1); // Write SMALL reports. @@ -294,15 +293,16 @@ TEST_CASE("short report") { "added_removed_symbols_only_short_diff"})); SECTION(test.name) { - stg::Metrics metrics; + std::ostringstream os; + stg::Runtime runtime(os, false); // Read inputs. stg::Graph graph; - const auto id0 = Read(graph, stg::InputFormat::ABI, test.xml0, metrics); - const auto id1 = Read(graph, stg::InputFormat::ABI, test.xml1, metrics); + const auto id0 = Read(runtime, graph, stg::InputFormat::ABI, test.xml0); + const auto id1 = Read(runtime, graph, stg::InputFormat::ABI, test.xml1); // Compute differences. - stg::Compare compare{graph, {}, metrics}; + stg::Compare compare{runtime, graph, {}}; const auto& [equals, comparison] = compare(id0, id1); // Write SHORT reports. @@ -325,14 +325,15 @@ TEST_CASE("short report") { } TEST_CASE("fidelity diff") { - stg::Metrics metrics; + std::ostringstream os; + stg::Runtime runtime(os, false); // Read inputs. stg::Graph graph; const auto id0 = - Read(graph, stg::InputFormat::STG, "fidelity_diff_0.stg", metrics); + Read(runtime, graph, stg::InputFormat::STG, "fidelity_diff_0.stg"); const auto id1 = - Read(graph, stg::InputFormat::STG, "fidelity_diff_1.stg", metrics); + Read(runtime, graph, stg::InputFormat::STG, "fidelity_diff_1.stg"); // Compute fidelity diff. auto fidelity_diff = stg::GetFidelityTransitions(graph, id0, id1); diff --git a/substitution.h b/substitution.h index 5d3ba55..67f74b4 100644 --- a/substitution.h +++ b/substitution.h @@ -91,11 +91,11 @@ struct Substitute { Update(x.type_id); } - void operator()(Member& x) { + void operator()(Method& x) { Update(x.type_id); } - void operator()(Method& x) { + void operator()(Member& x) { Update(x.type_id); } diff --git a/test_cases/info_tests/template/expected/template_parameter_cc.elf_stg b/test_cases/info_tests/template/expected/template_parameter_cc.elf_stg new file mode 100644 index 0000000..f44fb07 --- /dev/null +++ b/test_cases/info_tests/template/expected/template_parameter_cc.elf_stg @@ -0,0 +1,210 @@ +version: 0x00000002 +root_id: 0x84ea5130 +primitive { + id: 0x6720d32f + name: "int" + encoding: SIGNED_INTEGER + bytesize: 0x00000004 +} +array { + id: 0x9d362140 + number_of_elements: 17 + element_type_id: 0x6720d32f +} +member { + id: 0x80bcb4cd + name: "a" + type_id: 0x6720d32f +} +member { + id: 0x4cff5574 + name: "b" + type_id: 0x6720d32f + offset: 32 +} +member { + id: 0x4cff584f + name: "b" + type_id: 0x6720d32f +} +member { + id: 0x0f3f9186 + name: "c" + type_id: 0x6720d32f + offset: 32 +} +member { + id: 0xc37de7d7 + name: "d" + type_id: 0x6720d32f + offset: 64 +} +member { + id: 0x874410b0 + name: "e" + type_id: 0x9d362140 +} +member { + id: 0x41fcdfad + name: "f" + type_id: 0x6720d32f + offset: 544 +} +member { + id: 0x023d43d4 + name: "g" + type_id: 0x6720d32f + offset: 576 +} +member { + id: 0xa8938b2e + name: "h" + type_id: 0x7578447d +} +member { + id: 0x86c14d13 + name: "i" + type_id: 0x6720d32f +} +member { + id: 0x2d952030 + name: "j" + type_id: 0xa5c1eab5 +} +member { + id: 0x6940985f + name: "k" + type_id: 0x6720d32f +} +member { + id: 0xc5d22295 + name: "l" + type_id: 0xcb978265 +} +member { + id: 0x8dbf3d30 + name: "m" + type_id: 0x6720d32f +} +member { + id: 0x46ee5fe7 + name: "n" + type_id: 0x8891f0f8 +} +member { + id: 0x04420382 + name: "o" + type_id: 0x6720d32f + offset: 608 +} +struct_union { + id: 0x7578447d + kind: STRUCT + name: "T1<int>" + definition { + bytesize: 8 + member_id: 0x80bcb4cd + member_id: 0x4cff5574 + } +} +struct_union { + id: 0xa5c1eab5 + kind: STRUCT + name: "T2<int, int>" + definition { + bytesize: 12 + member_id: 0x4cff584f + member_id: 0x0f3f9186 + member_id: 0xc37de7d7 + } +} +struct_union { + id: 0xcb978265 + kind: STRUCT + name: "T3<17, int>" + definition { + bytesize: 76 + member_id: 0x874410b0 + member_id: 0x41fcdfad + member_id: 0x023d43d4 + } +} +struct_union { + id: 0xd94d74b0 + kind: UNION + name: "T4<T1>" + definition { + bytesize: 8 + member_id: 0xa8938b2e + member_id: 0x86c14d13 + } +} +struct_union { + id: 0x5e0ea2b4 + kind: UNION + name: "T5<T2>" + definition { + bytesize: 12 + member_id: 0x2d952030 + member_id: 0x6940985f + } +} +struct_union { + id: 0x8891f0f8 + kind: UNION + name: "T6<T3>" + definition { + bytesize: 76 + member_id: 0xc5d22295 + member_id: 0x8dbf3d30 + } +} +struct_union { + id: 0x886d9bb6 + kind: STRUCT + name: "T7<T6>" + definition { + bytesize: 80 + member_id: 0x46ee5fe7 + member_id: 0x04420382 + } +} +elf_symbol { + id: 0xab18a5e8 + name: "v1" + is_defined: true + symbol_type: OBJECT + type_id: 0xd94d74b0 + full_name: "v1" +} +elf_symbol { + id: 0x975e59cf + name: "v2" + is_defined: true + symbol_type: OBJECT + type_id: 0x5e0ea2b4 + full_name: "v2" +} +elf_symbol { + id: 0x2999e447 + name: "v3" + is_defined: true + symbol_type: OBJECT + type_id: 0x8891f0f8 + full_name: "v3" +} +elf_symbol { + id: 0x19de9370 + name: "v4" + is_defined: true + symbol_type: OBJECT + type_id: 0x886d9bb6 + full_name: "v4" +} +interface { + id: 0x84ea5130 + symbol_id: 0xab18a5e8 + symbol_id: 0x975e59cf + symbol_id: 0x2999e447 + symbol_id: 0x19de9370 +} diff --git a/test_cases/info_tests/template/expected/value_parameter_cc.elf_stg b/test_cases/info_tests/template/expected/value_parameter_cc.elf_stg new file mode 100644 index 0000000..24f9d2d --- /dev/null +++ b/test_cases/info_tests/template/expected/value_parameter_cc.elf_stg @@ -0,0 +1,231 @@ +version: 0x00000002 +root_id: 0x84ea5130 +special { + id: 0x48b5725f + kind: VOID +} +pointer_reference { + id: 0xc3b63b14 + kind: LVALUE_REFERENCE + pointee_type_id: 0x10985193 +} +pointer_reference { + id: 0xf6e6592a + kind: LVALUE_REFERENCE + pointee_type_id: 0xc5d9d969 +} +qualified { + id: 0xc5d9d969 + qualifier: CONST + qualified_type_id: 0x6720d32f +} +primitive { + id: 0x6720d32f + name: "int" + encoding: SIGNED_INTEGER + bytesize: 0x00000004 +} +struct_union { + id: 0x04d4366e + kind: STRUCT + name: "S<&H::i2>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x8fd5ff58 + kind: STRUCT + name: "S<&H::j2>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0xe4d3d1c9 + kind: STRUCT + name: "S<&a>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x3a75138f + kind: STRUCT + name: "S<&d>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x31dac610 + kind: STRUCT + name: "S<&e>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x54943dbe + kind: STRUCT + name: "S<\'p\'>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x2119c209 + kind: STRUCT + name: "S<(K)0>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x3bb4a383 + kind: STRUCT + name: "S<15>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x0e0f27de + kind: STRUCT + name: "S<4>" + definition { + bytesize: 1 + } +} +struct_union { + id: 0x0257c3b9 + kind: STRUCT + name: "S<nullptr>" + definition { + bytesize: 1 + } +} +function { + id: 0x10985193 + return_type_id: 0x48b5725f +} +elf_symbol { + id: 0x65e66866 + name: "_Z1ev" + is_defined: true + symbol_type: FUNCTION + type_id: 0x10985193 + full_name: "e" +} +elf_symbol { + id: 0x2230fb28 + name: "c" + is_defined: true + symbol_type: OBJECT + type_id: 0xf6e6592a + full_name: "c" +} +elf_symbol { + id: 0xe0778f95 + name: "f" + is_defined: true + symbol_type: OBJECT + type_id: 0xc3b63b14 + full_name: "f" +} +elf_symbol { + id: 0xea07c015 + name: "v_char" + is_defined: true + symbol_type: OBJECT + type_id: 0x54943dbe + full_name: "v_char" +} +elf_symbol { + id: 0xc31d4af7 + name: "v_enumerator" + is_defined: true + symbol_type: OBJECT + type_id: 0x2119c209 + full_name: "v_enumerator" +} +elf_symbol { + id: 0x87e5aa8a + name: "v_function_pointer" + is_defined: true + symbol_type: OBJECT + type_id: 0x3a75138f + full_name: "v_function_pointer" +} +elf_symbol { + id: 0x2c4cacfa + name: "v_function_reference" + is_defined: true + symbol_type: OBJECT + type_id: 0x31dac610 + full_name: "v_function_reference" +} +elf_symbol { + id: 0x40715147 + name: "v_int" + is_defined: true + symbol_type: OBJECT + type_id: 0x3bb4a383 + full_name: "v_int" +} +elf_symbol { + id: 0x575714fe + name: "v_int_pointer" + is_defined: true + symbol_type: OBJECT + type_id: 0xe4d3d1c9 + full_name: "v_int_pointer" +} +elf_symbol { + id: 0xe323633f + name: "v_int_reference" + is_defined: true + symbol_type: OBJECT + type_id: 0x0e0f27de + full_name: "v_int_reference" +} +elf_symbol { + id: 0x481dfc0e + name: "v_nullptr" + is_defined: true + symbol_type: OBJECT + type_id: 0x0257c3b9 + full_name: "v_nullptr" +} +elf_symbol { + id: 0xcb9e41aa + name: "v_pointer_to_member" + is_defined: true + symbol_type: OBJECT + type_id: 0x04d4366e + full_name: "v_pointer_to_member" +} +elf_symbol { + id: 0x3dc86173 + name: "v_pointer_to_method" + is_defined: true + symbol_type: OBJECT + type_id: 0x8fd5ff58 + full_name: "v_pointer_to_method" +} +interface { + id: 0x84ea5130 + symbol_id: 0x65e66866 + symbol_id: 0x2230fb28 + symbol_id: 0xe0778f95 + symbol_id: 0xea07c015 + symbol_id: 0xc31d4af7 + symbol_id: 0x87e5aa8a + symbol_id: 0x2c4cacfa + symbol_id: 0x40715147 + symbol_id: 0x575714fe + symbol_id: 0xe323633f + symbol_id: 0x481dfc0e + symbol_id: 0xcb9e41aa + symbol_id: 0x3dc86173 +} diff --git a/test_cases/info_tests/template/template_parameter.cc b/test_cases/info_tests/template/template_parameter.cc new file mode 100644 index 0000000..2fc54f0 --- /dev/null +++ b/test_cases/info_tests/template/template_parameter.cc @@ -0,0 +1,37 @@ +template<typename A1> struct T1 { + A1 a; + int b; +}; +template<typename B1, typename B2> struct T2 { + B1 b; + B2 c; + int d; +}; +template<int C1, typename C2> struct T3 { + int e[C1]; + C2 f; + int g; +}; + +template<template<typename> typename P1> union T4{ + P1<int> h; + int i; +}; +template<template<typename, typename> typename P2> union T5{ + P2<int, int> j; + int k; +}; +template<template<auto, typename> typename P3> union T6{ + P3<17, int> l; + int m; +}; + +template<template<template<auto, typename> typename> typename P4> struct T7 { + P4<T3> n; + int o; +}; + +T4<T1> v1; +T5<T2> v2; +T6<T3> v3; +T7<T6> v4; diff --git a/test_cases/info_tests/template/value_parameter.cc b/test_cases/info_tests/template/value_parameter.cc new file mode 100644 index 0000000..3aa7b1f --- /dev/null +++ b/test_cases/info_tests/template/value_parameter.cc @@ -0,0 +1,41 @@ +template<auto V> struct S {}; + +S<15> v_int; +S<'p'> v_char; +// Not supported by Clang yet. +//S<4.2> v_double; + +extern const int a; +S<&a> v_int_pointer; + +const int b = 4; +const int& c = b; +S<c> v_int_reference; + +extern void d(); +S<&d> v_function_pointer; + +void e() {} +void (&f)() = e; +S<f> v_function_reference; + +struct H { + int i1; + int i2; + void j1(); + void j2(); +}; +S<&H::i2> v_pointer_to_member; +S<&H::j2> v_pointer_to_method; + +enum class K { l }; +S<K::l> v_enumerator; + +S<nullptr> v_nullptr; + +// C++20 only. +//int m[2] = { 10, 4 }; +//S<&(m[1])> v_pointer_to_subobject; + +// C++20 only and cannot appear in ABI anyway. +//S<[](){}> v_lambda; diff --git a/testdata/abigail_duplicate_types_9.xml b/testdata/abigail_duplicate_types_9.xml new file mode 100644 index 0000000..a447fd3 --- /dev/null +++ b/testdata/abigail_duplicate_types_9.xml @@ -0,0 +1,28 @@ +<!-- Output from libabigail, split into two corpora --> +<abi-corpus-group> + <abi-corpus version='2.1' architecture='elf-amd-x86_64'> + <elf-variable-symbols> + <elf-symbol name='t' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> + </elf-variable-symbols> + </abi-corpus> + <abi-corpus version='2.1' architecture='elf-amd-x86_64'> + <abi-instr address-size='64' path='mt.cc' language='LANG_C_plus_plus_14'> + <type-decl name='int' size-in-bits='32' id='95e97e5e'/> + <namespace-decl name='N'> + <class-decl name='S' size-in-bits='8' is-struct='yes' visibility='default' id='af2c111e'> + <member-type access='public'> + <class-decl name='T' size-in-bits='32' is-struct='yes' visibility='default' id='500730c2'> + <data-member access='public' layout-offset-in-bits='0'> + <var-decl name='x' type-id='95e97e5e' visibility='default'/> + </data-member> + <data-member access='public' layout-offset-in-bits='32'> + <var-decl name='y' type-id='95e97e5e' visibility='default'/> + </data-member> + </class-decl> + </member-type> + </class-decl> + </namespace-decl> + <var-decl name='t' type-id='500730c2' mangled-name='t' visibility='default' elf-symbol-id='t'/> + </abi-instr> + </abi-corpus> +</abi-corpus-group> diff --git a/type_normalisation.cc b/type_normalisation.cc index 50a59b1..377198f 100644 --- a/type_normalisation.cc +++ b/type_normalisation.cc @@ -118,11 +118,11 @@ struct FindQualifiedTypesAndFunctions { (*this)(x.type_id); } - void operator()(const Member& x, Id) { + void operator()(const Method& x, Id) { (*this)(x.type_id); } - void operator()(const Method& x, Id) { + void operator()(const Member& x, Id) { (*this)(x.type_id); } diff --git a/type_resolution.cc b/type_resolution.cc index b32d535..928332c 100644 --- a/type_resolution.cc +++ b/type_resolution.cc @@ -26,7 +26,7 @@ #include <vector> #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "unification.h" namespace stg { @@ -35,13 +35,13 @@ namespace { // Collect named type definition and declaration nodes. struct NamedTypes { - NamedTypes(const Graph& graph, Metrics& metrics) + NamedTypes(Runtime& runtime, const Graph& graph) : graph(graph), seen(Id(0)), - nodes(metrics, "named_types.nodes"), - types(metrics, "named_types.types"), - definitions(metrics, "named_types.definitions"), - declarations(metrics, "named_types.declarations") { + nodes(runtime, "named_types.nodes"), + types(runtime, "named_types.types"), + definitions(runtime, "named_types.definitions"), + declarations(runtime, "named_types.declarations") { seen.Reserve(graph.Limit()); } @@ -184,24 +184,24 @@ struct NamedTypes { } // namespace -void ResolveTypes(Graph& graph, Unification& unification, - const std::vector<Id>& roots, Metrics& metrics) { - const Time total(metrics, "resolve.total"); +void ResolveTypes(Runtime& runtime, Graph& graph, Unification& unification, + const std::vector<Id>& roots) { + const Time total(runtime, "resolve.total"); // collect named types - NamedTypes named_types(graph, metrics); + NamedTypes named_types(runtime, graph); { - const Time time(metrics, "resolve.collection"); + const Time time(runtime, "resolve.collection"); for (const Id& root : roots) { named_types(root); } } { - const Time time(metrics, "resolve.unification"); - Counter definition_unified(metrics, "resolve.definition.unified"); - Counter definition_not_unified(metrics, "resolve.definition.not_unified"); - Counter declaration_unified(metrics, "resolve.declaration.unified"); + const Time time(runtime, "resolve.unification"); + Counter definition_unified(runtime, "resolve.definition.unified"); + Counter definition_not_unified(runtime, "resolve.definition.not_unified"); + Counter declaration_unified(runtime, "resolve.declaration.unified"); for (auto& [_, info] : named_types.type_info) { // try to unify the type definitions auto& definitions = info.definitions; diff --git a/type_resolution.h b/type_resolution.h index 326b241..1d511d0 100644 --- a/type_resolution.h +++ b/type_resolution.h @@ -23,13 +23,13 @@ #include <vector> #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "unification.h" namespace stg { -void ResolveTypes(Graph& graph, Unification& unification, - const std::vector<Id>& roots, Metrics& metrics); +void ResolveTypes(Runtime& runtime, Graph& graph, Unification& unification, + const std::vector<Id>& roots); } // namespace stg diff --git a/unification.h b/unification.h index 3f6ee9a..d8fde2e 100644 --- a/unification.h +++ b/unification.h @@ -25,7 +25,7 @@ #include <unordered_set> #include "graph.h" -#include "metrics.h" +#include "runtime.h" #include "substitution.h" namespace stg { @@ -34,15 +34,15 @@ namespace stg { // destruction. class Unification { public: - Unification(Graph& graph, Id start, Metrics& metrics) + Unification(Runtime& runtime, Graph& graph, Id start) : graph_(graph), start_(start), mapping_(start), - metrics_(metrics), - find_query_(metrics, "unification.find_query"), - find_halved_(metrics, "unification.find_halved"), - union_known_(metrics, "unification.union_known"), - union_unknown_(metrics, "unification.union_unknown") {} + runtime_(runtime), + find_query_(runtime, "unification.find_query"), + find_halved_(runtime, "unification.find_halved"), + union_known_(runtime, "unification.union_known"), + union_unknown_(runtime, "unification.union_unknown") {} ~Unification() { if (std::uncaught_exceptions() > 0) { @@ -50,9 +50,9 @@ class Unification { return; } // apply substitutions to the entire graph - const Time time(metrics_, "unification.rewrite"); - Counter removed(metrics_, "unification.removed"); - Counter retained(metrics_, "unification.retained"); + const Time time(runtime_, "unification.rewrite"); + Counter removed(runtime_, "unification.removed"); + Counter retained(runtime_, "unification.retained"); auto remap = [&](Id& id) { Update(id); }; @@ -118,7 +118,7 @@ class Unification { Graph& graph_; Id start_; DenseIdMapping mapping_; - Metrics& metrics_; + Runtime& runtime_; Counter find_query_; Counter find_halved_; Counter union_known_; |