blob: 3aa1c13e2890d036c2a51632cd05928fb12d98e6 [file] [log] [blame]
//===-- HashedNameToDIE.h ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef SymbolFileDWARF_HashedNameToDIE_h_
#define SymbolFileDWARF_HashedNameToDIE_h_
#include <vector>
#include "DWARFDefines.h"
#include "DWARFFormValue.h"
#include "lldb/lldb-defines.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/MappedHash.h"
class SymbolFileDWARF;
class DWARFCompileUnit;
class DWARFDebugInfoEntry;
struct DWARFMappedHash
{
struct DIEInfo
{
dw_offset_t offset; // The DIE offset
dw_tag_t tag;
uint32_t type_flags; // Any flags for this DIEInfo
DIEInfo () :
offset (DW_INVALID_OFFSET),
tag (0),
type_flags (0)
{
}
DIEInfo (dw_offset_t o, dw_tag_t t, uint32_t f) :
offset(o),
tag (t),
type_flags (f)
{
}
void
Clear()
{
offset = DW_INVALID_OFFSET;
tag = 0;
type_flags = 0;
}
};
typedef std::vector<DIEInfo> DIEInfoArray;
typedef std::vector<uint32_t> DIEArray;
static void
ExtractDIEArray (const DIEInfoArray &die_info_array,
DIEArray &die_offsets)
{
const size_t count = die_info_array.size();
for (size_t i=0; i<count; ++i)
{
die_offsets.push_back (die_info_array[i].offset);
}
}
static void
ExtractDIEArray (const DIEInfoArray &die_info_array,
const dw_tag_t tag,
DIEArray &die_offsets)
{
if (tag == 0)
{
ExtractDIEArray (die_info_array, die_offsets);
}
else
{
const size_t count = die_info_array.size();
for (size_t i=0; i<count; ++i)
{
const dw_tag_t die_tag = die_info_array[i].tag;
bool tag_matches = die_tag == 0 || tag == die_tag;
if (!tag_matches)
{
if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
}
if (tag_matches)
die_offsets.push_back (die_info_array[i].offset);
}
}
}
enum AtomType
{
eAtomTypeNULL = 0u,
eAtomTypeDIEOffset = 1u, // DIE offset, check form for encoding
eAtomTypeCUOffset = 2u, // DIE offset of the compiler unit header that contains the item in question
eAtomTypeTag = 3u, // DW_TAG_xxx value, should be encoded as DW_FORM_data1 (if no tags exceed 255) or DW_FORM_data2
eAtomTypeNameFlags = 4u, // Flags from enum NameFlags
eAtomTypeTypeFlags = 5u // Flags from enum TypeFlags
};
// Bit definitions for the eAtomTypeTypeFlags flags
enum TypeFlags
{
// Always set for C++, only set for ObjC if this is the
// @implementation for class
eTypeFlagClassIsImplementation = ( 1u << 1 )
};
static void
ExtractClassOrStructDIEArray (const DIEInfoArray &die_info_array,
bool return_implementation_only_if_available,
DIEArray &die_offsets)
{
const size_t count = die_info_array.size();
for (size_t i=0; i<count; ++i)
{
const dw_tag_t die_tag = die_info_array[i].tag;
if (die_tag == 0 || die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
{
if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation)
{
if (return_implementation_only_if_available)
{
// We found the one true definiton for this class, so
// only return that
die_offsets.clear();
die_offsets.push_back (die_info_array[i].offset);
return;
}
else
{
// Put the one true definition as the first entry so it
// matches first
die_offsets.insert (die_offsets.begin(), die_info_array[i].offset);
}
}
else
{
die_offsets.push_back (die_info_array[i].offset);
}
}
}
}
static void
ExtractTypesFromDIEArray (const DIEInfoArray &die_info_array,
uint32_t type_flag_mask,
uint32_t type_flag_value,
DIEArray &die_offsets)
{
const size_t count = die_info_array.size();
for (size_t i=0; i<count; ++i)
{
if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value)
die_offsets.push_back (die_info_array[i].offset);
}
}
struct Atom
{
uint16_t type;
dw_form_t form;
Atom (uint16_t t = eAtomTypeNULL, dw_form_t f = 0) :
type (t),
form (f)
{
}
};
typedef std::vector<Atom> AtomArray;
static uint32_t
GetTypeFlags (SymbolFileDWARF *dwarf2Data,
const DWARFCompileUnit* cu,
const DWARFDebugInfoEntry* die);
static const char *
GetAtomTypeName (uint16_t atom)
{
switch (atom)
{
case eAtomTypeNULL: return "NULL";
case eAtomTypeDIEOffset: return "die-offset";
case eAtomTypeCUOffset: return "cu-offset";
case eAtomTypeTag: return "die-tag";
case eAtomTypeNameFlags: return "name-flags";
case eAtomTypeTypeFlags: return "type-flags";
}
return "<invalid>";
}
struct Prologue
{
// DIE offset base so die offsets in hash_data can be CU relative
dw_offset_t die_base_offset;
AtomArray atoms;
uint32_t atom_mask;
size_t min_hash_data_byte_size;
bool hash_data_has_fixed_byte_size;
Prologue (dw_offset_t _die_base_offset = 0) :
die_base_offset (_die_base_offset),
atoms(),
atom_mask (0),
min_hash_data_byte_size(0),
hash_data_has_fixed_byte_size(true)
{
// Define an array of DIE offsets by first defining an array,
// and then define the atom type for the array, in this case
// we have an array of DIE offsets
AppendAtom (eAtomTypeDIEOffset, DW_FORM_data4);
}
virtual ~Prologue()
{
}
void
ClearAtoms ()
{
hash_data_has_fixed_byte_size = true;
min_hash_data_byte_size = 0;
atom_mask = 0;
atoms.clear();
}
bool
ContainsAtom (AtomType atom_type) const
{
return (atom_mask & (1u << atom_type)) != 0;
}
virtual void
Clear ()
{
die_base_offset = 0;
ClearAtoms ();
}
void
AppendAtom (AtomType type, dw_form_t form)
{
atoms.push_back (Atom(type, form));
atom_mask |= 1u << type;
switch (form)
{
case DW_FORM_indirect:
case DW_FORM_exprloc:
case DW_FORM_flag_present:
case DW_FORM_ref_sig8:
assert (!"Unhandled atom form");
break;
case DW_FORM_string:
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_sdata:
case DW_FORM_udata:
case DW_FORM_ref_udata:
hash_data_has_fixed_byte_size = false;
// Fall through to the cases below...
case DW_FORM_flag:
case DW_FORM_data1:
case DW_FORM_ref1:
case DW_FORM_sec_offset:
min_hash_data_byte_size += 1;
break;
case DW_FORM_block2:
hash_data_has_fixed_byte_size = false;
// Fall through to the cases below...
case DW_FORM_data2:
case DW_FORM_ref2:
min_hash_data_byte_size += 2;
break;
case DW_FORM_block4:
hash_data_has_fixed_byte_size = false;
// Fall through to the cases below...
case DW_FORM_data4:
case DW_FORM_ref4:
case DW_FORM_addr:
case DW_FORM_ref_addr:
case DW_FORM_strp:
min_hash_data_byte_size += 4;
break;
case DW_FORM_data8:
case DW_FORM_ref8:
min_hash_data_byte_size += 8;
break;
}
}
// void
// Dump (std::ostream* ostrm_ptr);
uint32_t
Read (const lldb_private::DataExtractor &data, uint32_t offset)
{
ClearAtoms ();
die_base_offset = data.GetU32 (&offset);
const uint32_t atom_count = data.GetU32 (&offset);
if (atom_count == 0x00060003u)
{
// Old format, deal with contents of old pre-release format
while (data.GetU32(&offset))
/* do nothing */;
// Hardcode to the only known value for now.
AppendAtom (eAtomTypeDIEOffset, DW_FORM_data4);
}
else
{
for (uint32_t i=0; i<atom_count; ++i)
{
AtomType type = (AtomType)data.GetU16 (&offset);
dw_form_t form = (dw_form_t)data.GetU16 (&offset);
AppendAtom (type, form);
}
}
return offset;
}
// virtual void
// Write (BinaryStreamBuf &s);
size_t
GetByteSize () const
{
// Add an extra count to the atoms size for the zero termination Atom that gets
// written to disk
return sizeof(die_base_offset) + sizeof(uint32_t) + atoms.size() * sizeof(Atom);
}
size_t
GetMinumumHashDataByteSize () const
{
return min_hash_data_byte_size;
}
bool
HashDataHasFixedByteSize() const
{
return hash_data_has_fixed_byte_size;
}
};
struct Header : public MappedHash::Header<Prologue>
{
Header (dw_offset_t _die_base_offset = 0)
{
}
virtual
~Header()
{
}
virtual size_t
GetByteSize (const HeaderData &header_data)
{
return header_data.GetByteSize();
}
// virtual void
// Dump (std::ostream* ostrm_ptr);
//
virtual uint32_t
Read (lldb_private::DataExtractor &data, uint32_t offset)
{
offset = MappedHash::Header<Prologue>::Read (data, offset);
if (offset != UINT32_MAX)
{
offset = header_data.Read (data, offset);
}
return offset;
}
bool
Read (const lldb_private::DataExtractor &data,
uint32_t *offset_ptr,
DIEInfo &hash_data) const
{
const size_t num_atoms = header_data.atoms.size();
if (num_atoms == 0)
return false;
for (size_t i=0; i<num_atoms; ++i)
{
DWARFFormValue form_value (header_data.atoms[i].form);
if (!form_value.ExtractValue(data, offset_ptr, NULL))
return false;
switch (header_data.atoms[i].type)
{
case eAtomTypeDIEOffset: // DIE offset, check form for encoding
hash_data.offset = form_value.Reference (header_data.die_base_offset);
break;
case eAtomTypeTag: // DW_TAG value for the DIE
hash_data.tag = form_value.Unsigned ();
case eAtomTypeTypeFlags: // Flags from enum TypeFlags
hash_data.type_flags = form_value.Unsigned ();
break;
default:
return false;
break;
}
}
return true;
}
void
Dump (lldb_private::Stream& strm, const DIEInfo &hash_data) const
{
const size_t num_atoms = header_data.atoms.size();
for (size_t i=0; i<num_atoms; ++i)
{
if (i > 0)
strm.PutCString (", ");
DWARFFormValue form_value (header_data.atoms[i].form);
switch (header_data.atoms[i].type)
{
case eAtomTypeDIEOffset: // DIE offset, check form for encoding
strm.Printf ("{0x%8.8x}", hash_data.offset);
break;
case eAtomTypeTag: // DW_TAG value for the DIE
{
const char *tag_cstr = lldb_private::DW_TAG_value_to_name (hash_data.tag);
if (tag_cstr)
strm.PutCString (tag_cstr);
else
strm.Printf ("DW_TAG_(0x%4.4x)", hash_data.tag);
}
break;
case eAtomTypeTypeFlags: // Flags from enum TypeFlags
strm.Printf ("0x%2.2x", hash_data.type_flags);
if (hash_data.type_flags)
{
strm.PutCString (" (");
if (hash_data.type_flags & eTypeFlagClassIsImplementation)
strm.PutCString (" implementation");
strm.PutCString (" )");
}
break;
default:
strm.Printf ("AtomType(0x%x)", header_data.atoms[i].type);
break;
}
}
}
};
// class ExportTable
// {
// public:
// ExportTable ();
//
// void
// AppendNames (DWARFDebugPubnamesSet &pubnames_set,
// StringTable &string_table);
//
// void
// AppendNamesEntry (SymbolFileDWARF *dwarf2Data,
// const DWARFCompileUnit* cu,
// const DWARFDebugInfoEntry* die,
// StringTable &string_table);
//
// void
// AppendTypesEntry (DWARFData *dwarf2Data,
// const DWARFCompileUnit* cu,
// const DWARFDebugInfoEntry* die,
// StringTable &string_table);
//
// size_t
// Save (BinaryStreamBuf &names_data, const StringTable &string_table);
//
// void
// AppendName (const char *name,
// uint32_t die_offset,
// StringTable &string_table,
// dw_offset_t name_debug_str_offset = DW_INVALID_OFFSET); // If "name" has already been looked up, then it can be supplied
// void
// AppendType (const char *name,
// uint32_t die_offset,
// StringTable &string_table);
//
//
// protected:
// struct Entry
// {
// uint32_t hash;
// uint32_t str_offset;
// uint32_t die_offset;
// };
//
// // Map uniqued .debug_str offset to the corresponding DIE offsets
// typedef std::map<uint32_t, DIEInfoArray> NameInfo;
// // Map a name hash to one or more name infos
// typedef std::map<uint32_t, NameInfo> BucketEntry;
//
// static uint32_t
// GetByteSize (const NameInfo &name_info);
//
// typedef std::vector<BucketEntry> BucketEntryColl;
// typedef std::vector<Entry> EntryColl;
// EntryColl m_entries;
//
// };
// A class for reading and using a saved hash table from a block of data
// in memory
class MemoryTable : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header, DIEInfoArray>
{
public:
MemoryTable (lldb_private::DataExtractor &table_data,
const lldb_private::DataExtractor &string_table,
const char *name) :
MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray> (table_data),
m_data (table_data),
m_string_table (string_table),
m_name (name)
{
}
virtual
~MemoryTable ()
{
}
virtual const char *
GetStringForKeyType (KeyType key) const
{
// The key in the DWARF table is the .debug_str offset for the string
return m_string_table.PeekCStr (key);
}
virtual Result
GetHashDataForName (const char *name,
uint32_t* hash_data_offset_ptr,
Pair &pair) const
{
pair.key = m_data.GetU32 (hash_data_offset_ptr);
pair.value.clear();
// If the key is zero, this terminates our chain of HashData objects
// for this hash value.
if (pair.key == 0)
return eResultEndOfHashData;
// There definitely should be a string for this string offset, if
// there isn't, there is something wrong, return and error
const char *strp_cstr = m_string_table.PeekCStr (pair.key);
if (strp_cstr == NULL)
{
*hash_data_offset_ptr = UINT32_MAX;
return eResultError;
}
const uint32_t count = m_data.GetU32 (hash_data_offset_ptr);
const uint32_t min_total_hash_data_size = count * m_header.header_data.GetMinumumHashDataByteSize();
if (count > 0 && m_data.ValidOffsetForDataOfSize (*hash_data_offset_ptr, min_total_hash_data_size))
{
// We have at least one HashData entry, and we have enough
// data to parse at leats "count" HashData enties.
// First make sure the entire C string matches...
const bool match = strcmp (name, strp_cstr) == 0;
if (!match && m_header.header_data.HashDataHasFixedByteSize())
{
// If the string doesn't match and we have fixed size data,
// we can just add the total byte size of all HashData objects
// to the hash data offset and be done...
*hash_data_offset_ptr += min_total_hash_data_size;
}
else
{
// If the string does match, or we don't have fixed size data
// then we need to read the hash data as a stream. If the
// string matches we also append all HashData objects to the
// value array.
for (uint32_t i=0; i<count; ++i)
{
DIEInfo die_info;
if (m_header.Read(m_data, hash_data_offset_ptr, die_info))
{
// Only happend the HashData if the string matched...
if (match)
pair.value.push_back (die_info);
}
else
{
// Something went wrong while reading the data
*hash_data_offset_ptr = UINT32_MAX;
return eResultError;
}
}
}
// Return the correct response depending on if the string matched
// or not...
if (match)
return eResultKeyMatch; // The key (cstring) matches and we have lookup results!
else
return eResultKeyMismatch; // The key doesn't match, this function will get called
// again for the next key/value or the key terminator
// which in our case is a zero .debug_str offset.
}
else
{
*hash_data_offset_ptr = UINT32_MAX;
return eResultError;
}
}
virtual Result
AppendHashDataForRegularExpression (const lldb_private::RegularExpression& regex,
uint32_t* hash_data_offset_ptr,
Pair &pair) const
{
pair.key = m_data.GetU32 (hash_data_offset_ptr);
// If the key is zero, this terminates our chain of HashData objects
// for this hash value.
if (pair.key == 0)
return eResultEndOfHashData;
// There definitely should be a string for this string offset, if
// there isn't, there is something wrong, return and error
const char *strp_cstr = m_string_table.PeekCStr (pair.key);
if (strp_cstr == NULL)
return eResultError;
const uint32_t count = m_data.GetU32 (hash_data_offset_ptr);
const uint32_t min_total_hash_data_size = count * m_header.header_data.GetMinumumHashDataByteSize();
if (count > 0 && m_data.ValidOffsetForDataOfSize (*hash_data_offset_ptr, min_total_hash_data_size))
{
const bool match = regex.Execute(strp_cstr);
if (!match && m_header.header_data.HashDataHasFixedByteSize())
{
// If the regex doesn't match and we have fixed size data,
// we can just add the total byte size of all HashData objects
// to the hash data offset and be done...
*hash_data_offset_ptr += min_total_hash_data_size;
}
else
{
// If the string does match, or we don't have fixed size data
// then we need to read the hash data as a stream. If the
// string matches we also append all HashData objects to the
// value array.
for (uint32_t i=0; i<count; ++i)
{
DIEInfo die_info;
if (m_header.Read(m_data, hash_data_offset_ptr, die_info))
{
// Only happend the HashData if the string matched...
if (match)
pair.value.push_back (die_info);
}
else
{
// Something went wrong while reading the data
*hash_data_offset_ptr = UINT32_MAX;
return eResultError;
}
}
}
// Return the correct response depending on if the string matched
// or not...
if (match)
return eResultKeyMatch; // The key (cstring) matches and we have lookup results!
else
return eResultKeyMismatch; // The key doesn't match, this function will get called
// again for the next key/value or the key terminator
// which in our case is a zero .debug_str offset.
}
else
{
*hash_data_offset_ptr = UINT32_MAX;
return eResultError;
}
}
size_t
AppendAllDIEsThatMatchingRegex (const lldb_private::RegularExpression& regex,
DIEInfoArray &die_info_array) const
{
const uint32_t hash_count = m_header.hashes_count;
Pair pair;
for (uint32_t offset_idx=0; offset_idx<hash_count; ++offset_idx)
{
uint32_t hash_data_offset = GetHashDataOffset (offset_idx);
while (hash_data_offset != UINT32_MAX)
{
const uint32_t prev_hash_data_offset = hash_data_offset;
Result hash_result = AppendHashDataForRegularExpression (regex, &hash_data_offset, pair);
if (prev_hash_data_offset == hash_data_offset)
break;
// Check the result of getting our hash data
switch (hash_result)
{
case eResultKeyMatch:
case eResultKeyMismatch:
// Whether we matches or not, it doesn't matter, we
// keep looking.
break;
case eResultEndOfHashData:
case eResultError:
hash_data_offset = UINT32_MAX;
break;
}
}
}
die_info_array.swap (pair.value);
return die_info_array.size();
}
size_t
AppendAllDIEsInRange (const uint32_t die_offset_start,
const uint32_t die_offset_end,
DIEInfoArray &die_info_array) const
{
const uint32_t hash_count = m_header.hashes_count;
for (uint32_t offset_idx=0; offset_idx<hash_count; ++offset_idx)
{
bool done = false;
uint32_t hash_data_offset = GetHashDataOffset (offset_idx);
while (!done && hash_data_offset != UINT32_MAX)
{
KeyType key = m_data.GetU32 (&hash_data_offset);
// If the key is zero, this terminates our chain of HashData objects
// for this hash value.
if (key == 0)
break;
const uint32_t count = m_data.GetU32 (&hash_data_offset);
for (uint32_t i=0; i<count; ++i)
{
DIEInfo die_info;
if (m_header.Read(m_data, &hash_data_offset, die_info))
{
if (die_info.offset == 0)
done = true;
if (die_offset_start <= die_info.offset && die_info.offset < die_offset_end)
die_info_array.push_back(die_info);
}
}
}
}
return die_info_array.size();
}
size_t
FindByName (const char *name, DIEArray &die_offsets)
{
DIEInfoArray die_info_array;
if (FindByName(name, die_info_array))
DWARFMappedHash::ExtractDIEArray (die_info_array, die_offsets);
return die_info_array.size();
}
size_t
FindByNameAndTag (const char *name,
const dw_tag_t tag,
DIEArray &die_offsets)
{
DIEInfoArray die_info_array;
if (FindByName(name, die_info_array))
DWARFMappedHash::ExtractDIEArray (die_info_array, tag, die_offsets);
return die_info_array.size();
}
size_t
FindCompleteObjCClassByName (const char *name, DIEArray &die_offsets, bool must_be_implementation)
{
DIEInfoArray die_info_array;
if (FindByName(name, die_info_array))
{
if (must_be_implementation && GetHeader().header_data.ContainsAtom (eAtomTypeTypeFlags))
{
// If we have two atoms, then we have the DIE offset and
// the type flags so we can find the objective C class
// efficiently.
DWARFMappedHash::ExtractTypesFromDIEArray (die_info_array,
UINT32_MAX,
eTypeFlagClassIsImplementation,
die_offsets);
}
else
{
// We don't only want the one true definition, so try and see
// what we can find, and only return class or struct DIEs.
// If we do have the full implementation, then return it alone,
// else return all possible matches.
const bool return_implementation_only_if_available = true;
DWARFMappedHash::ExtractClassOrStructDIEArray (die_info_array,
return_implementation_only_if_available,
die_offsets);
}
}
return die_offsets.size();
}
size_t
FindByName (const char *name, DIEInfoArray &die_info_array)
{
Pair kv_pair;
size_t old_size = die_info_array.size();
if (Find (name, kv_pair))
{
die_info_array.swap(kv_pair.value);
return die_info_array.size() - old_size;
}
return 0;
}
protected:
const lldb_private::DataExtractor &m_data;
const lldb_private::DataExtractor &m_string_table;
std::string m_name;
};
};
#endif // SymbolFileDWARF_HashedNameToDIE_h_