Skip to content

Commit 772a75b

Browse files
Improved typedef handling (#85)
* Improvement to handle anonymous typedefs * adding fix for issue 84 * adding test case for anonymous struct conflict * adding a special case for `objc_object`
1 parent 9b8f884 commit 772a75b

File tree

9 files changed

+132
-35
lines changed

9 files changed

+132
-35
lines changed

include/orc/hash.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ inline std::size_t hash_combine(std::size_t seed, const T& x) {
3737
// This is the terminating hash_combine call when there's only one item left to hash into the
3838
// seed. It also serves as the combiner the other routine variant uses to generate its new
3939
// seed.
40-
return (seed ^ x) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
40+
return seed ^ (x + 0x9e3779b9 + (seed << 6) + (seed >> 2));
4141
}
4242

4343
template <class T, class... Args>

include/orc/orc.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ void register_dies(dies die_vector);
6363

6464
std::string to_json(const std::vector<odrv_report>&);
6565

66+
std::string version_json();
67+
6668
} // namespace orc
6769

6870
void orc_reset();

src/dwarf.cpp

+81-17
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,15 @@ void line_header::read(freader& s, bool needs_byteswap) {
341341
}
342342

343343
/**************************************************************************************************/
344-
345-
std::size_t fatal_attribute_hash(const attribute_sequence& attributes) {
346-
#if ORC_FEATURE(PROFILE_DIE_DETAILS)
347-
ZoneScoped;
348-
#endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
349-
350-
// We only hash the attributes that could contribute to an ODRV. We also sort that set of
351-
// attributes by name to make sure the hashing is consistent regardless of attribute order.
352-
constexpr const std::size_t max_names_k{32};
353-
std::array<dw::at, max_names_k> names{dw::at::none};
344+
// It is fixed to keep allocations from happening.
345+
constexpr std::size_t max_names_k{32};
346+
using fixed_attribute_array = std::array<dw::at, max_names_k>;
347+
348+
// for an incoming set of arbitrary attributes, return the subset of those that are fatal.
349+
// We also sort the set of attributes by name to make sure subsequent traversal of this set
350+
// is consistent.
351+
fixed_attribute_array fatal_attributes_within(const attribute_sequence& attributes) {
352+
fixed_attribute_array names{dw::at::none};
354353
std::size_t count{0};
355354

356355
for (const auto& attr : attributes) {
@@ -362,17 +361,32 @@ std::size_t fatal_attribute_hash(const attribute_sequence& attributes) {
362361
}
363362
std::sort(&names[0], &names[count]);
364363

364+
return names;
365+
}
366+
367+
/**************************************************************************************************/
368+
369+
std::size_t fatal_attribute_hash(const attribute_sequence& attributes) {
370+
#if ORC_FEATURE(PROFILE_DIE_DETAILS)
371+
ZoneScoped;
372+
#endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
373+
374+
// We only hash the attributes that could contribute to an ODRV. That's what makes them "fatal"
375+
365376
std::size_t h{0};
366377

367-
for (std::size_t i{0}; i < count; ++i) {
378+
for (auto name : fatal_attributes_within(attributes)) {
379+
// As soon as we hit our first no-name-attribute, we're done.
380+
if (name == dw::at::none) break;
381+
368382
// If this assert fires, it means an attribute's value was passed over during evaluation,
369383
// but it was necessary for ODRV evaluation after all. The fix is to improve the attribute
370384
// form evaluation engine such that this attribute's value is no longer passed over.
371-
const auto name = names[i];
372385
const auto& attribute = attributes.get(name);
373386
assert(!attributes.has(name, attribute_value::type::passover));
374-
const auto attribute_hash = attribute._value.hash();
375-
h = orc::hash_combine(h, attribute_hash);
387+
388+
const auto hash = attribute._value.hash();
389+
h = orc::hash_combine(h, hash);
376390
}
377391

378392
return h;
@@ -474,6 +488,7 @@ struct dwarf::implementation {
474488
std::vector<pool_string> _decl_files;
475489
std::unordered_map<std::size_t, pool_string> _type_cache;
476490
std::unordered_map<std::size_t, pool_string> _debug_str_cache;
491+
pool_string _last_typedef_name; // for unnamed structs - see https://github.com/adobe/orc/issues/84
477492
cu_header _cu_header;
478493
std::size_t _cu_header_offset{0}; // offset of the compilation unit header. Relative to __debug_info.
479494
std::size_t _cu_die_offset{0}; // offset of the `compile_unit` die. Relative to start of `debug_info`
@@ -1380,6 +1395,18 @@ pool_string dwarf::implementation::resolve_type(attribute type) {
13801395
result = empool("const " + recurse(attributes).allocate_string());
13811396
} else if (die._tag == dw::tag::pointer_type) {
13821397
result = empool(recurse(attributes).allocate_string() + "*");
1398+
} else if (die._tag == dw::tag::typedef_) {
1399+
if (const auto maybe_type = recurse(attributes)) {
1400+
result = maybe_type;
1401+
} else if (attributes.has_string(dw::at::name)) {
1402+
result = attributes.string(dw::at::name);
1403+
} else {
1404+
// `result` will be empty if we hit this in release
1405+
// builds. It's bad, but not UB, so the program can
1406+
// continue from here (though the results will be
1407+
// untrustworthy).
1408+
assert(!"Got a typedef with no name?");
1409+
}
13831410
} else if (attributes.has_string(dw::at::type)) {
13841411
result = type.string();
13851412
} else if (attributes.has_reference(dw::at::type)) {
@@ -1416,6 +1443,7 @@ die_pair dwarf::implementation::abbreviation_to_die(std::size_t die_address, pro
14161443
die._tag = a._tag;
14171444
die._has_children = a._has_children;
14181445

1446+
// Can we get rid of this memory allocation? This happens a lot...
14191447
attributes.reserve(a._attributes.size());
14201448

14211449
std::transform(a._attributes.begin(), a._attributes.end(), std::back_inserter(attributes),
@@ -1425,8 +1453,9 @@ die_pair dwarf::implementation::abbreviation_to_die(std::size_t die_address, pro
14251453
});
14261454

14271455
if (mode == process_mode::complete) {
1456+
// These statements must be kept in sync with the ones dealing with issue 84 below.
1457+
// See https://github.com/adobe/orc/issues/84
14281458
path_identifier_set(die_identifier(die, attributes));
1429-
14301459
die._path = empool(std::string_view(qualified_symbol_name(die, attributes)));
14311460
}
14321461

@@ -1507,6 +1536,16 @@ bool dwarf::implementation::skip_die(die& d, const attribute_sequence& attribute
15071536
return true;
15081537
}
15091538

1539+
// There are some symbols that are owned by the compiler and/or OS vendor (read: Apple)
1540+
// that have been observed to conflict. We skip them for our purposes, as we have no
1541+
// control over them.
1542+
if (d._path.view().find("::[u]::objc_object") == 0) {
1543+
#if ORC_FEATURE(PROFILE_DIE_DETAILS)
1544+
ZoneTextL("skipping: vendor- or compiler-defined symbol");
1545+
#endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
1546+
return true;
1547+
}
1548+
15101549
// lambdas are ephemeral and can't cause (hopefully) an ODRV
15111550
if (d._path.view().find("lambda") != std::string::npos) {
15121551
#if ORC_FEATURE(PROFILE_DIE_DETAILS)
@@ -1652,12 +1691,37 @@ void dwarf::implementation::process_all_dies() {
16521691
}
16531692
}
16541693

1694+
post_process_die_attributes(attributes);
1695+
1696+
// See https://github.com/adobe/orc/issues/84
1697+
// This code accounts for unnamed structs that are part of
1698+
// a typedef expression. In such case the typedef actually adds a name
1699+
// for the purpose of linking, but the structure's
1700+
// die does not contain the name. Furthermore, the typedef die has
1701+
// been found to precede the die for the structure. So we grab it if
1702+
// we find a typedef, and use it if an ensuing structure_type has no name.
1703+
if (die._tag == dw::tag::typedef_ && attributes.has(dw::at::name)) {
1704+
_last_typedef_name = attributes.get(dw::at::name).string();
1705+
} else if (die._tag == dw::tag::structure_type &&
1706+
!attributes.has(dw::at::name) &&
1707+
_last_typedef_name) {
1708+
attribute name;
1709+
name._name = dw::at::name;
1710+
name._form = dw::form::strp;
1711+
name._value.string(_last_typedef_name);
1712+
attributes.push_back(name);
1713+
1714+
// These two statements must be in sync with the ones in `abbreviation_to_die`
1715+
path_identifier_set(die_identifier(die, attributes));
1716+
die._path = empool(std::string_view(qualified_symbol_name(die, attributes)));
1717+
1718+
_last_typedef_name = pool_string(); // reset it to avoid misuse
1719+
}
1720+
16551721
if (die._has_children) {
16561722
path_identifier_push();
16571723
}
16581724

1659-
post_process_die_attributes(attributes);
1660-
16611725
die._skippable = skip_die(die, attributes);
16621726
die._ofd_index = _ofd_index;
16631727
die._hash = die_hash(die, attributes);

src/main.cpp

+15-9
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,19 @@ auto epilogue(bool exception) {
451451
const auto& g = globals::instance();
452452

453453
if (g._object_file_count == 0) {
454-
cout_safe([&](auto& s) {
455-
const auto local_build = ORC_VERSION_STR() == std::string("local");
456-
const std::string tag_url = local_build ? "" : std::string(" (https://github.com/adobe/orc/releases/tag/") + ORC_VERSION_STR() + ")";
457-
s << "ORC (https://github.com/adobe/orc)\n";
458-
s << " version: " << ORC_VERSION_STR() << tag_url << '\n';
459-
s << " sha: " << ORC_SHA_STR() << '\n';
460-
});
454+
if (settings::instance()._output_file_mode == settings::output_file_mode::json) {
455+
cout_safe([](auto& s){
456+
s << orc::version_json() << '\n';
457+
});
458+
} else {
459+
cout_safe([&](auto& s) {
460+
const auto local_build = ORC_VERSION_STR() == std::string("local");
461+
const std::string tag_url = local_build ? "" : std::string(" (https://github.com/adobe/orc/releases/tag/") + ORC_VERSION_STR() + ")";
462+
s << "ORC (https://github.com/adobe/orc)\n";
463+
s << " version: " << ORC_VERSION_STR() << tag_url << '\n';
464+
s << " sha: " << ORC_SHA_STR() << '\n';
465+
});
466+
}
461467
} else if (log_level_at_least(settings::log_level::warning)) {
462468
// Make sure these values are in sync with the `synopsis` json blob in `to_json`.
463469
cout_safe([&](auto& s) {
@@ -510,9 +516,9 @@ void maybe_forward_to_linker(int argc, char** argv, const cmdline_results& cmdli
510516
} else if (cmdline._libtool_mode) {
511517
executable_path /= "libtool";
512518
} else {
513-
if (log_level_at_least(settings::log_level::warning)) {
519+
if (log_level_at_least(settings::log_level::verbose)) {
514520
cout_safe([&](auto& s) {
515-
s << "warning: libtool/ld mode could not be derived; forwarding to linker disabled\n";
521+
s << "verbose: libtool/ld mode could not be derived; forwarding to linker disabled\n";
516522
});
517523
}
518524

src/orc.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "orc/str.hpp"
4747
#include "orc/string_pool.hpp"
4848
#include "orc/tracy.hpp"
49+
#include "orc/version.hpp"
4950

5051
/**************************************************************************************************/
5152

@@ -702,6 +703,15 @@ std::string to_json(const std::vector<odrv_report>& reports) {
702703
return result.dump(spaces_k);
703704
}
704705

706+
std::string version_json() {
707+
nlohmann::json result;
708+
709+
result["version"] = ORC_VERSION_STR();
710+
result["sha256"] = ORC_SHA_STR();
711+
712+
return result.dump();
713+
}
714+
705715
/**************************************************************************************************/
706716

707717
} // namespace orc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
typedef struct { double _d; } S;
2+
3+
// Required so the compiler generates a symbol.
4+
int getd(S s) { return s._d; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
typedef struct {int _i;} S;
2+
3+
// Required so the compiler generates a symbol.
4+
int geti(S s) { return s._i; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[[source]]
2+
path = "a.cpp"
3+
4+
[[source]]
5+
path = "b.cpp"
6+
7+
[[odrv]]
8+
category = "structure:byte_size"
9+
symbol = "S"

test/src/main.cpp

+6-8
Original file line numberDiff line numberDiff line change
@@ -504,19 +504,17 @@ void run_battery_test(const std::filesystem::path& home) {
504504

505505
/**************************************************************************************************/
506506

507-
void traverse_directory_tree(std::filesystem::path& directory) {
507+
void traverse_directory_tree(const std::filesystem::path& directory) {
508508
assert(is_directory(directory));
509509

510+
if (exists(directory / tomlname_k)) {
511+
run_battery_test(directory);
512+
}
513+
510514
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
511515
try {
512516
if (is_directory(entry)) {
513-
std::filesystem::path path = entry.path();
514-
515-
if (exists(path / tomlname_k)) {
516-
run_battery_test(path);
517-
}
518-
519-
traverse_directory_tree(path);
517+
traverse_directory_tree(entry.path());
520518
}
521519
} catch (...) {
522520
console_error() << "\nIn battery " << entry.path() << ":";

0 commit comments

Comments
 (0)