blob: a2c9f5f9db1f10b695c3ddfb0598640e7fa42cd5 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
#define ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
#include <cstdint>
#include <unordered_map>
#include "dwarf.h"
#include "leb128.h"
#include "writer.h"
namespace art {
namespace dwarf {
// 32-bit FNV-1a hash function which we use to find duplicate abbreviations.
// See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
template< typename Allocator >
struct FNVHash {
size_t operator()(const std::vector<uint8_t, Allocator>& v) const {
uint32_t hash = 2166136261u;
for (size_t i = 0; i < v.size(); i++) {
hash = (hash ^ v[i]) * 16777619u;
}
return hash;
}
};
/*
* Writer for debug information entries (DIE).
* It also handles generation of abbreviations.
*
* Usage:
* StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
* WriteStrp(DW_AT_producer, "Compiler name", debug_str);
* StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
* WriteStrp(DW_AT_name, "Foo", debug_str);
* EndTag();
* EndTag();
*/
template< typename Allocator = std::allocator<uint8_t> >
class DebugInfoEntryWriter FINAL : private Writer<Allocator> {
public:
// Start debugging information entry.
void StartTag(Tag tag, Children children) {
DCHECK(has_children) << "This tag can not have nested tags";
if (inside_entry_) {
// Write abbrev code for the previous entry.
this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
inside_entry_ = false;
}
StartAbbrev(tag, children);
// Abbrev code placeholder of sufficient size.
abbrev_code_offset_ = this->data()->size();
this->PushUleb128(NextAbbrevCode());
depth_++;
inside_entry_ = true;
has_children = (children == DW_CHILDREN_yes);
}
// End debugging information entry.
void EndTag() {
DCHECK_GT(depth_, 0);
if (inside_entry_) {
// Write abbrev code for this tag.
this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
inside_entry_ = false;
}
if (has_children) {
this->PushUint8(0); // End of children.
}
depth_--;
has_children = true; // Parent tag obviously has children.
}
void WriteAddr(Attribute attrib, uint64_t value) {
AddAbbrevAttribute(attrib, DW_FORM_addr);
patch_locations_.push_back(this->data()->size());
if (is64bit_) {
this->PushUint64(value);
} else {
this->PushUint32(value);
}
}
void WriteBlock(Attribute attrib, const void* ptr, int size) {
AddAbbrevAttribute(attrib, DW_FORM_block);
this->PushUleb128(size);
this->PushData(ptr, size);
}
void WriteData1(Attribute attrib, uint8_t value) {
AddAbbrevAttribute(attrib, DW_FORM_data1);
this->PushUint8(value);
}
void WriteData2(Attribute attrib, uint16_t value) {
AddAbbrevAttribute(attrib, DW_FORM_data2);
this->PushUint16(value);
}
void WriteData4(Attribute attrib, uint32_t value) {
AddAbbrevAttribute(attrib, DW_FORM_data4);
this->PushUint32(value);
}
void WriteData8(Attribute attrib, uint64_t value) {
AddAbbrevAttribute(attrib, DW_FORM_data8);
this->PushUint64(value);
}
void WriteSdata(Attribute attrib, int value) {
AddAbbrevAttribute(attrib, DW_FORM_sdata);
this->PushSleb128(value);
}
void WriteUdata(Attribute attrib, int value) {
AddAbbrevAttribute(attrib, DW_FORM_udata);
this->PushUleb128(value);
}
void WriteUdata(Attribute attrib, uint32_t value) {
AddAbbrevAttribute(attrib, DW_FORM_udata);
this->PushUleb128(value);
}
void WriteFlag(Attribute attrib, bool value) {
AddAbbrevAttribute(attrib, DW_FORM_flag);
this->PushUint8(value ? 1 : 0);
}
void WriteRef4(Attribute attrib, int cu_offset) {
AddAbbrevAttribute(attrib, DW_FORM_ref4);
this->PushUint32(cu_offset);
}
void WriteRef(Attribute attrib, int cu_offset) {
AddAbbrevAttribute(attrib, DW_FORM_ref_udata);
this->PushUleb128(cu_offset);
}
void WriteString(Attribute attrib, const char* value) {
AddAbbrevAttribute(attrib, DW_FORM_string);
this->PushString(value);
}
void WriteStrp(Attribute attrib, int address) {
AddAbbrevAttribute(attrib, DW_FORM_strp);
this->PushUint32(address);
}
void WriteStrp(Attribute attrib, const char* value, std::vector<uint8_t>* debug_str) {
AddAbbrevAttribute(attrib, DW_FORM_strp);
int address = debug_str->size();
debug_str->insert(debug_str->end(), value, value + strlen(value) + 1);
this->PushUint32(address);
}
bool Is64bit() const { return is64bit_; }
const std::vector<uintptr_t>& GetPatchLocations() const {
return patch_locations_;
}
using Writer<Allocator>::data;
DebugInfoEntryWriter(bool is64bitArch,
std::vector<uint8_t, Allocator>* debug_abbrev,
const Allocator& alloc = Allocator())
: Writer<Allocator>(&entries_),
debug_abbrev_(debug_abbrev),
current_abbrev_(alloc),
abbrev_codes_(alloc),
entries_(alloc),
is64bit_(is64bitArch) {
debug_abbrev_.PushUint8(0); // Add abbrev table terminator.
}
~DebugInfoEntryWriter() {
DCHECK_EQ(depth_, 0);
}
private:
// Start abbreviation declaration.
void StartAbbrev(Tag tag, Children children) {
DCHECK(!inside_entry_);
current_abbrev_.clear();
EncodeUnsignedLeb128(&current_abbrev_, tag);
current_abbrev_.push_back(children);
}
// Add attribute specification.
void AddAbbrevAttribute(Attribute name, Form type) {
DCHECK(inside_entry_) << "Call StartTag before adding attributes.";
EncodeUnsignedLeb128(&current_abbrev_, name);
EncodeUnsignedLeb128(&current_abbrev_, type);
}
int NextAbbrevCode() {
return 1 + abbrev_codes_.size();
}
// End abbreviation declaration and return its code.
int EndAbbrev() {
DCHECK(inside_entry_);
auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_),
NextAbbrevCode()));
int abbrev_code = it.first->second;
if (UNLIKELY(it.second)) { // Inserted new entry.
const std::vector<uint8_t, Allocator>& abbrev = it.first->first;
debug_abbrev_.Pop(); // Remove abbrev table terminator.
debug_abbrev_.PushUleb128(abbrev_code);
debug_abbrev_.PushData(abbrev.data(), abbrev.size());
debug_abbrev_.PushUint8(0); // Attribute list end.
debug_abbrev_.PushUint8(0); // Attribute list end.
debug_abbrev_.PushUint8(0); // Add abbrev table terminator.
}
return abbrev_code;
}
private:
// Fields for writing and deduplication of abbrevs.
Writer<Allocator> debug_abbrev_;
std::vector<uint8_t, Allocator> current_abbrev_;
std::unordered_map<std::vector<uint8_t, Allocator>, int,
FNVHash<Allocator> > abbrev_codes_;
// Fields for writing of debugging information entries.
std::vector<uint8_t, Allocator> entries_;
bool is64bit_;
int depth_ = 0;
size_t abbrev_code_offset_ = 0; // Location to patch once we know the code.
bool inside_entry_ = false; // Entry ends at first child (if any).
bool has_children = true;
std::vector<uintptr_t> patch_locations_;
};
} // namespace dwarf
} // namespace art
#endif // ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_