1111#include " utils/error.hpp"
1212#include " utils/utils.hpp"
1313#include " utils/lru_cache.hpp"
14+ #include " utils/io/file.hpp"
1415#include " platform/path.hpp"
1516#include " platform/program_name.hpp" // For CPPTRACE_MAX_PATH
1617#include " logging.hpp"
@@ -904,53 +905,112 @@ namespace libdwarf {
904905 }
905906 }
906907
908+ optional<std::string> get_dwo_path (const die_object& cu_die, std::string dwo_name) {
909+ if (is_absolute (dwo_name)) {
910+ return dwo_name;
911+ }
912+ if (auto comp_dir = cu_die.get_string_attribute (DW_AT_comp_dir)) {
913+ return comp_dir.unwrap () + PATH_SEP + dwo_name;
914+ }
915+ // maybe default to dwo_name but for now not doing anything
916+ return nullopt ;
917+ }
918+
919+ Result<bool , internal_error> is_clang_pcm_file (cstring_view dwo_path) {
920+ // Clang AST file magic bytes are CPCH:
921+ // https://github.com/llvm/llvm-project/blob/main/clang/lib/Serialization/ASTWriter.cpp#L5483-L5487
922+ auto file_res = file::open (dwo_path);
923+ if (file_res.is_error ()) {
924+ return std::move (file_res).unwrap_error ();
925+ }
926+ auto & file_obj = file_res.unwrap_value ();
927+ auto magic = file_obj.read <std::array<char , 4 >>(0 );
928+ if (magic.is_error ()) {
929+ return std::move (magic).unwrap_error ();
930+ }
931+ return magic.unwrap_value () == std::array<char , 4 >{' C' , ' P' , ' C' , ' H' };
932+ }
933+
907934 void perform_dwarf_fission_resolution (
908935 const die_object& cu_die,
909- const optional<std::string>& dwo_name ,
936+ cstring_view dwo_path ,
910937 const object_frame& object_frame_info,
911938 stacktrace_frame& frame,
912939 std::vector<stacktrace_frame>& inlines
913940 ) {
914941 // Split dwarf / debug fission / dwo is handled here
915942 // Location of the split full CU is a combination of DW_AT_dwo_name/DW_AT_GNU_dwo_name and DW_AT_comp_dir
916943 // https://gcc.gnu.org/wiki/DebugFission
917- if (dwo_name) {
918- // TODO: DWO ID?
919- auto comp_dir = cu_die.get_string_attribute (DW_AT_comp_dir);
920- Dwarf_Half offset_size = 0 ;
921- Dwarf_Half dwversion = 0 ;
922- dwarf_get_version_of_die (cu_die.get (), &dwversion, &offset_size);
923- std::string path;
924- if (is_absolute (dwo_name.unwrap ())) {
925- path = dwo_name.unwrap ();
926- } else if (comp_dir) {
927- path = comp_dir.unwrap () + PATH_SEP + dwo_name.unwrap ();
928- } else {
929- // maybe default to dwo_name but for now not doing anything
930- return ;
944+ // TODO: DWO ID?
945+ Dwarf_Half offset_size = 0 ;
946+ Dwarf_Half dwversion = 0 ;
947+ dwarf_get_version_of_die (cu_die.get (), &dwversion, &offset_size);
948+ // todo: slight inefficiency in this copy-back strategy due to other frame members
949+ frame_with_inlines res;
950+ if (get_cache_mode () == cache_mode::prioritize_memory) {
951+ dwarf_resolver resolver (
952+ dwo_path,
953+ skeleton_info{cu_die.clone (), dwversion, *this }
954+ );
955+ res = resolver.resolve_frame (object_frame_info);
956+ } else {
957+ auto off = cu_die.get_global_offset ();
958+ auto it = split_full_cu_resolvers.find (off);
959+ if (it == split_full_cu_resolvers.end ()) {
960+ it = split_full_cu_resolvers.emplace (
961+ off,
962+ detail::make_unique<dwarf_resolver>(
963+ dwo_path,
964+ skeleton_info{cu_die.clone (), dwversion, *this }
965+ )
966+ ).first ;
931967 }
932- // todo: slight inefficiency in this copy-back strategy due to other frame members
933- frame_with_inlines res;
934- if (get_cache_mode () == cache_mode::prioritize_memory) {
935- dwarf_resolver resolver (
936- path,
937- skeleton_info{cu_die.clone (), dwversion, *this }
938- );
939- res = resolver.resolve_frame (object_frame_info);
940- } else {
941- auto off = cu_die.get_global_offset ();
942- auto it = split_full_cu_resolvers.find (off);
943- if (it == split_full_cu_resolvers.end ()) {
944- it = split_full_cu_resolvers.emplace (
945- off,
946- detail::make_unique<dwarf_resolver>(path, skeleton_info{cu_die.clone (), dwversion, *this })
947- ).first ;
948- }
949- res = it->second ->resolve_frame (object_frame_info);
968+ res = it->second ->resolve_frame (object_frame_info);
969+ }
970+ frame = std::move (res.frame );
971+ inlines = std::move (res.inlines );
972+ }
973+
974+ // returns true if the cu looks like it's debug fission, informs resolve_frame_core to not go down the normal
975+ // path
976+ bool try_perform_dwarf_fission_resolution (
977+ const die_object& cu_die,
978+ const object_frame& object_frame_info,
979+ stacktrace_frame& frame,
980+ std::vector<stacktrace_frame>& inlines
981+ ) {
982+ // gnu non-standard debug-fission may create non-skeleton CU DIEs and just add dwo attributes
983+ // clang emits dwo names in the split CUs, so guard against going down the dwarf fission path (which
984+ // doesn't infinitely recurse because it's not emitted as an absolute path and there's no comp dir but
985+ // it's good to guard against the infinite recursion anyway)
986+ auto dwo_name = get_dwo_name (cu_die);
987+ if (dwo_name && !skeleton) {
988+ auto dwo_path = get_dwo_path (cu_die, dwo_name.unwrap ());
989+ if (!dwo_path) {
990+ return true ;
950991 }
951- frame = std::move (res.frame );
952- inlines = std::move (res.inlines );
992+ // Clang -gmodules emits a CU with a DW_AT_dwo_name reference to a .pcm file, which is a clang ast file.
993+ // The reference is just for type information, function information is still written to the object
994+ // file's dwarf. Even though the CU seems to be generated with no range information and so lookup_cu
995+ // should never find this CU, let's check if the file looks like a clang ast file and if so not go down
996+ // the fission path.
997+ auto res = is_clang_pcm_file (dwo_path.unwrap ());
998+ if (res.is_error ()) {
999+ // if res is an error either the file couldn't be found or four bytes from it couldn't be read,
1000+ // libdwarf will probably error too but we'll continue down the fission path anyway
1001+ res.drop_error ();
1002+ } else if (res.unwrap_value ()) {
1003+ // if this is a clang pcm file ignore it, it doesn't contain dwarf
1004+ return false ;
1005+ }
1006+ perform_dwarf_fission_resolution (cu_die, dwo_path.unwrap (), object_frame_info, frame, inlines);
1007+ return true ;
1008+ }
1009+ if (cu_die.get_tag () == DW_TAG_skeleton_unit) {
1010+ // It's possible for a DW_TAG_skeleton_unit to exist with no dwo name
1011+ return true ;
9531012 }
1013+ return false ;
9541014 }
9551015
9561016 CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
@@ -967,17 +1027,12 @@ namespace libdwarf {
9671027 optional<cu_info> cu = lookup_cu (pc);
9681028 if (cu) {
9691029 const auto & cu_die = cu.unwrap ().cu_die .get ();
970- // gnu non-standard debug-fission may create non-skeleton CU DIEs and just add dwo attributes
971- // clang emits dwo names in the split CUs, so guard against going down the dwarf fission path (which
972- // doesn't infinitely recurse because it's not emitted as an absolute path and there's no comp dir but
973- // it's good to guard against the infinite recursion anyway)
974- auto dwo_name = get_dwo_name (cu_die);
975- if (cu_die.get_tag () == DW_TAG_skeleton_unit || (dwo_name && !skeleton)) {
976- perform_dwarf_fission_resolution (cu_die, dwo_name, object_frame_info, frame, inlines);
977- } else {
978- retrieve_line_info (cu_die, pc, frame);
979- retrieve_symbol (cu_die, pc, cu.unwrap ().dwversion , frame, inlines);
1030+ if (try_perform_dwarf_fission_resolution (cu_die, object_frame_info, frame, inlines)) {
1031+ return ;
9801032 }
1033+ // normal path: resolve in this CU
1034+ retrieve_line_info (cu_die, pc, frame);
1035+ retrieve_symbol (cu_die, pc, cu.unwrap ().dwversion , frame, inlines);
9811036 }
9821037 }
9831038
0 commit comments