blob: 115b903c6c7f95d3a0ee5d7f9d986c4801ada197 [file] [log] [blame]
//===-- LVLocation.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This implements the LVOperation and LVLocation classes.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
using namespace llvm;
using namespace llvm::logicalview;
#define DEBUG_TYPE "Location"
void LVOperation::print(raw_ostream &OS, bool Full) const {}
// Identify the most common type of operations and print them using a high
// level format, trying to isolate the DWARF complexity.
std::string LVOperation::getOperandsDWARFInfo() {
std::string String;
raw_string_ostream Stream(String);
auto PrintRegisterInfo = [&](LVSmall Code) {
//-----------------------------------------------------------------------
// 2.5.1.1 Literal encodings.
//-----------------------------------------------------------------------
if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) {
Stream << format("lit%d", Code - dwarf::DW_OP_lit0);
return;
}
//-----------------------------------------------------------------------
// 2.5.1.2 Register values.
//-----------------------------------------------------------------------
if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) {
std::string RegisterName(getReader().getRegisterName(Code, Operands));
Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0],
RegisterName.c_str());
return;
}
//-----------------------------------------------------------------------
// 2.6.1.1.3 Register location descriptions.
//-----------------------------------------------------------------------
if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) {
std::string RegisterName(getReader().getRegisterName(Code, Operands));
Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0,
RegisterName.c_str());
return;
}
Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " "
<< hexString(Operands[1]) << "#";
};
switch (Opcode) {
//-------------------------------------------------------------------------
// 2.5.1.1 Literal encodings.
//-------------------------------------------------------------------------
case dwarf::DW_OP_addr:
Stream << "addr " << hexString(Operands[0]);
break;
case dwarf::DW_OP_constu:
case dwarf::DW_OP_const1u:
case dwarf::DW_OP_const2u:
case dwarf::DW_OP_const4u:
case dwarf::DW_OP_const8u:
Stream << "const_u " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_consts:
case dwarf::DW_OP_const1s:
case dwarf::DW_OP_const2s:
case dwarf::DW_OP_const4s:
case dwarf::DW_OP_const8s:
Stream << "const_s " << int(Operands[0]);
break;
case dwarf::DW_OP_addrx:
Stream << "addrx " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_constx:
Stream << "constx " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_const_type:
Stream << "TODO: DW_OP_const_type";
break;
//-------------------------------------------------------------------------
// 2.5.1.2 Register values.
//-------------------------------------------------------------------------
case dwarf::DW_OP_fbreg:
Stream << "fbreg " << int(Operands[0]);
break;
case dwarf::DW_OP_bregx: {
std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(),
unsigned(Operands[1]));
break;
}
case dwarf::DW_OP_regval_type: {
std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(),
unsigned(Operands[1]));
break;
}
//-------------------------------------------------------------------------
// 2.5.1.3 Stack operations.
//-------------------------------------------------------------------------
case dwarf::DW_OP_dup:
Stream << "dup";
break;
case dwarf::DW_OP_drop:
Stream << "drop";
break;
case dwarf::DW_OP_pick:
Stream << "pick " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_over:
Stream << "over";
break;
case dwarf::DW_OP_swap:
Stream << "swap";
break;
case dwarf::DW_OP_rot:
Stream << "rot";
break;
case dwarf::DW_OP_deref:
Stream << "deref";
break;
case dwarf::DW_OP_deref_size:
Stream << "deref_size " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_deref_type:
Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset "
<< hexString(Operands[1]);
break;
case dwarf::DW_OP_xderef:
Stream << "xderef";
break;
case dwarf::DW_OP_xderef_size:
Stream << "xderef_size " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_xderef_type:
Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset "
<< hexString(Operands[1]);
break;
case dwarf::DW_OP_push_object_address:
Stream << "push_object_address";
break;
case dwarf::DW_OP_form_tls_address:
Stream << "form_tls_address " << hexString(Operands[0]);
break;
case dwarf::DW_OP_call_frame_cfa:
Stream << "call_frame_cfa";
break;
//-------------------------------------------------------------------------
// 2.5.1.4 Arithmetic and Logical Operations.
//-------------------------------------------------------------------------
case dwarf::DW_OP_abs:
Stream << "abs";
break;
case dwarf::DW_OP_and:
Stream << "and";
break;
case dwarf::DW_OP_div:
Stream << "div";
break;
case dwarf::DW_OP_minus:
Stream << "minus";
break;
case dwarf::DW_OP_mod:
Stream << "mod";
break;
case dwarf::DW_OP_mul:
Stream << "mul";
break;
case dwarf::DW_OP_neg:
Stream << "neg";
break;
case dwarf::DW_OP_not:
Stream << "not";
break;
case dwarf::DW_OP_or:
Stream << "or";
break;
case dwarf::DW_OP_plus:
Stream << "plus";
break;
case dwarf::DW_OP_plus_uconst:
Stream << "plus_uconst " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_shl:
Stream << "shl";
break;
case dwarf::DW_OP_shr:
Stream << "shr";
break;
case dwarf::DW_OP_shra:
Stream << "shra";
break;
case dwarf::DW_OP_xor:
Stream << "xor";
break;
//-------------------------------------------------------------------------
// 2.5.1.5 Control Flow Operations.
//-------------------------------------------------------------------------
case dwarf::DW_OP_le:
Stream << "le";
break;
case dwarf::DW_OP_ge:
Stream << "ge";
break;
case dwarf::DW_OP_eq:
Stream << "eq";
break;
case dwarf::DW_OP_lt:
Stream << "lt";
break;
case dwarf::DW_OP_gt:
Stream << "gt";
break;
case dwarf::DW_OP_ne:
Stream << "ne";
break;
case dwarf::DW_OP_skip:
Stream << "skip " << signed(Operands[0]);
break;
case dwarf::DW_OP_bra:
Stream << "bra " << signed(Operands[0]);
break;
case dwarf::DW_OP_call2:
Stream << "call2 DIE offset " << hexString(Operands[0]);
break;
case dwarf::DW_OP_call4:
Stream << "call4 DIE offset " << hexString(Operands[0]);
break;
case dwarf::DW_OP_call_ref:
Stream << "call_ref DIE offset " << hexString(Operands[0]);
break;
//-------------------------------------------------------------------------
// 2.5.1.6 Type Conversions.
//-------------------------------------------------------------------------
case dwarf::DW_OP_convert:
Stream << "convert DIE offset " << hexString(Operands[0]);
break;
case dwarf::DW_OP_reinterpret:
Stream << "reinterpret DIE offset " << hexString(Operands[0]);
break;
//-------------------------------------------------------------------------
// 2.5.1.7 Special Operations.
//-------------------------------------------------------------------------
case dwarf::DW_OP_nop:
Stream << "nop";
break;
case dwarf::DW_OP_entry_value:
Stream << "TODO: DW_OP_entry_value";
break;
//-------------------------------------------------------------------------
// 2.6.1.1.3 Register location descriptions.
//-------------------------------------------------------------------------
case dwarf::DW_OP_regx:
Stream << "regx" << getReader().getRegisterName(Opcode, Operands);
break;
//-------------------------------------------------------------------------
// 2.6.1.1.4 Implicit location descriptions.
//-------------------------------------------------------------------------
case dwarf::DW_OP_stack_value:
Stream << "stack_value";
break;
case dwarf::DW_OP_implicit_value:
Stream << "TODO: DW_OP_implicit_value";
break;
case dwarf::DW_OP_implicit_pointer:
Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " "
<< int(Operands[1]);
break;
//-------------------------------------------------------------------------
// 2.6.1.2 Composite location descriptions.
//-------------------------------------------------------------------------
case dwarf::DW_OP_piece:
Stream << "piece " << int(Operands[0]);
break;
case dwarf::DW_OP_bit_piece:
Stream << "bit_piece " << int(Operands[0]) << " offset "
<< int(Operands[1]);
break;
//-------------------------------------------------------------------------
// GNU extensions.
//-------------------------------------------------------------------------
case dwarf::DW_OP_GNU_entry_value:
Stream << "gnu_entry_value ";
PrintRegisterInfo(dwarf::DW_OP_reg0);
break;
case dwarf::DW_OP_GNU_push_tls_address:
Stream << "gnu_push_tls_address " << hexString(Operands[0]);
break;
case dwarf::DW_OP_GNU_addr_index:
Stream << "gnu_addr_index " << unsigned(Operands[0]);
break;
case dwarf::DW_OP_GNU_const_index:
Stream << "gnu_const_index " << unsigned(Operands[0]);
break;
//-------------------------------------------------------------------------
// Member location.
//-------------------------------------------------------------------------
case LVLocationMemberOffset:
Stream << "offset " << int(Operands[0]);
break;
//-------------------------------------------------------------------------
// Missing location.
//-------------------------------------------------------------------------
case dwarf::DW_OP_hi_user:
Stream << "missing";
break;
//-------------------------------------------------------------------------
// Register values.
//-------------------------------------------------------------------------
default:
PrintRegisterInfo(Opcode);
break;
}
return String;
}
// Identify the most common type of operations and print them using a high
// level format, trying to isolate the CodeView complexity.
std::string LVOperation::getOperandsCodeViewInfo() {
std::string String;
raw_string_ostream Stream(String);
// Get original CodeView operation code.
uint16_t OperationCode = getCodeViewOperationCode(Opcode);
switch (OperationCode) {
// Operands: [Offset, 0].
case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL:
Stream << "frame_pointer_rel " << int(Operands[0]);
break;
case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
Stream << "frame_pointer_rel_full_scope " << int(Operands[0]);
break;
// Operands: [Register, 0].
case codeview::SymbolKind::S_DEFRANGE_REGISTER:
Stream << "register " << getReader().getRegisterName(Opcode, Operands);
break;
case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER:
Stream << "subfield_register "
<< getReader().getRegisterName(Opcode, Operands);
break;
// Operands: [Register, Offset].
case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL:
Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands)
<< " offset " << int(Operands[1]);
break;
// Operands: [Program, 0].
case codeview::SymbolKind::S_DEFRANGE:
Stream << "frame " << int(Operands[0]);
break;
case codeview::SymbolKind::S_DEFRANGE_SUBFIELD:
Stream << "subfield " << int(Operands[0]);
break;
default:
Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " "
<< hexString(Operands[1]) << "#";
break;
}
return String;
}
namespace {
const char *const KindBaseClassOffset = "BaseClassOffset";
const char *const KindBaseClassStep = "BaseClassStep";
const char *const KindClassOffset = "ClassOffset";
const char *const KindFixedAddress = "FixedAddress";
const char *const KindMissingInfo = "Missing";
const char *const KindOperation = "Operation";
const char *const KindOperationList = "OperationList";
const char *const KindRegister = "Register";
const char *const KindUndefined = "Undefined";
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// DWARF location information.
//===----------------------------------------------------------------------===//
const char *LVLocation::kind() const {
const char *Kind = KindUndefined;
if (getIsBaseClassOffset())
Kind = KindBaseClassOffset;
else if (getIsBaseClassStep())
Kind = KindBaseClassStep;
else if (getIsClassOffset())
Kind = KindClassOffset;
else if (getIsFixedAddress())
Kind = KindFixedAddress;
else if (getIsGapEntry())
Kind = KindMissingInfo;
else if (getIsOperation())
Kind = KindOperation;
else if (getIsOperationList())
Kind = KindOperationList;
else if (getIsRegister())
Kind = KindRegister;
return Kind;
}
std::string LVLocation::getIntervalInfo() const {
static const char *const Question = "?";
std::string String;
raw_string_ostream Stream(String);
if (getIsAddressRange())
Stream << "{Range}";
auto PrintLine = [&](const LVLine *Line) {
if (Line) {
std::string TheLine;
TheLine = Line->lineNumberAsStringStripped();
Stream << TheLine.c_str();
} else {
Stream << Question;
}
};
Stream << " Lines ";
PrintLine(getLowerLine());
Stream << ":";
PrintLine(getUpperLine());
if (options().getAttributeOffset())
// Print the active range (low pc and high pc).
Stream << " [" << hexString(getLowerAddress()) << ":"
<< hexString(getUpperAddress()) << "]";
return String;
}
// Validate the ranges associated with the location.
bool LVLocation::validateRanges() {
// Traverse the locations and validate them against the address to line
// mapping in the current compile unit. Record those invalid ranges.
// A valid range must meet the following conditions:
// a) line(lopc) <= line(hipc)
// b) line(lopc) and line(hipc) are valid.
if (!hasAssociatedRange())
return true;
LVLineRange Range = getReaderCompileUnit()->lineRange(this);
LVLine *LowLine = Range.first;
LVLine *HighLine = Range.second;
if (LowLine)
setLowerLine(LowLine);
else {
setIsInvalidLower();
return false;
}
if (HighLine)
setUpperLine(HighLine);
else {
setIsInvalidUpper();
return false;
}
// Check for a valid interval.
if (LowLine->getLineNumber() > HighLine->getLineNumber()) {
setIsInvalidRange();
return false;
}
return true;
}
bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor,
float &Percentage) {
if (!options().getAttributeCoverage() && !Locations)
return false;
// Calculate the coverage depending on the kind of location. We have
// the simple and composed locations.
if (Locations->size() == 1) {
// Simple: fixed address, class offset, stack offset.
LVLocation *Location = Locations->front();
// Some types of locations do not have specific kind. Now is the time
// to set those types, depending on the operation type.
Location->updateKind();
if (Location->getIsLocationSimple()) {
Factor = 100;
Percentage = 100;
return true;
}
}
// Composed locations.
LVAddress LowerAddress = 0;
LVAddress UpperAddress = 0;
for (const LVLocation *Location : *Locations)
// Do not include locations representing a gap.
if (!Location->getIsGapEntry()) {
LowerAddress = Location->getLowerAddress();
UpperAddress = Location->getUpperAddress();
Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress
: LowerAddress - UpperAddress;
}
Percentage = 0;
return false;
}
void LVLocation::printRaw(raw_ostream &OS, bool Full) const {
// Print the active range (low pc and high pc).
OS << " [" << hexString(getLowerAddress()) << ":"
<< hexString(getUpperAddress()) << "]\n";
// Print any DWARF operations.
printRawExtra(OS, Full);
}
void LVLocation::printInterval(raw_ostream &OS, bool Full) const {
if (hasAssociatedRange())
OS << getIntervalInfo();
}
void LVLocation::print(raw_ostream &OS, bool Full) const {
if (getReader().doPrintLocation(this)) {
LVObject::print(OS, Full);
printExtra(OS, Full);
}
}
void LVLocation::printExtra(raw_ostream &OS, bool Full) const {
printInterval(OS, Full);
OS << "\n";
}
//===----------------------------------------------------------------------===//
// DWARF location for a symbol.
//===----------------------------------------------------------------------===//
// Add a Location Entry.
void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC,
LVUnsigned SectionOffset,
uint64_t LocDescOffset) {
setLowerAddress(LowPC);
setUpperAddress(HighPC);
// Record the offset where the location information begins.
setOffset(LocDescOffset ? LocDescOffset : SectionOffset);
// A -1 HighPC value, indicates no range.
if (HighPC == LVAddress(UINT64_MAX))
setIsDiscardedRange();
// Update the location kind, using the DWARF attribute.
setKind();
}
// Add a Location Record.
void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1,
LVUnsigned Operand2) {
if (!Entries)
Entries = new LVAutoOperations();
Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2));
}
// Based on the DWARF attribute, define the location kind.
void LVLocation::setKind() {
switch (getAttr()) {
case dwarf::DW_AT_data_member_location:
setIsClassOffset();
break;
case dwarf::DW_AT_location:
// Depending on the operand, we have a fixed address.
setIsFixedAddress();
break;
default:
break;
}
// For those symbols with absolute location information, ignore any
// gaps in their location description; that is the case with absolute
// memory addresses and members located at specific offsets.
if (hasAssociatedRange())
getParentSymbol()->setFillGaps();
}
void LVLocationSymbol::updateKind() {
// Update the location type for simple ones.
if (Entries && Entries->size() == 1) {
LVOperation *Operation = Entries->front();
if (dwarf::DW_OP_fbreg == Operation->getOpcode())
setIsStackOffset();
}
}
void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const {
if (Entries)
for (const LVOperation *Operation : *Entries)
Operation->print(OS, Full);
}
// Print location (formatted version).
void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) {
if (!Locations || Locations->empty())
return;
// Print the symbol coverage.
if (options().getAttributeCoverage()) {
// The location entries are contained within a symbol. Get a location,
// to access basic information about indentation, parent, etc.
LVLocation *Location = Locations->front();
LVSymbol *Symbol = Location->getParentSymbol();
float Percentage = Symbol->getCoveragePercentage();
// The coverage is dependent on the kind of location.
std::string String;
raw_string_ostream Stream(String);
Stream << format("%.2f%%", Percentage);
if (!Location->getIsLocationSimple())
Stream << format(" (%d/%d)", Symbol->getCoverageFactor(),
Symbol->getParentScope()->getCoverageFactor());
Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String),
/*UseQuotes=*/false,
/*PrintRef=*/false);
}
// Print the symbol location, including the missing entries.
if (getReader().doPrintLocation(/*Location=*/nullptr))
for (const LVLocation *Location : *Locations)
Location->print(OS, Full);
}
void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
OS << "{Location}";
if (getIsCallSite())
OS << " -> CallSite";
printInterval(OS, Full);
OS << "\n";
// Print location entries.
if (Full && Entries) {
bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation();
std::stringstream Stream;
std::string Leading = "";
for (LVOperation *Operation : *Entries) {
Stream << Leading
<< (CodeViewLocation ? Operation->getOperandsCodeViewInfo()
: Operation->getOperandsDWARFInfo());
Leading = ", ";
}
printAttributes(OS, Full, "{Entry} ", const_cast<LVLocationSymbol *>(this),
StringRef(Stream.str()),
/*UseQuotes=*/false,
/*PrintRef=*/false);
}
}