//===-- 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]. 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]. 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]. 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, ArrayRef Operands) { if (!Entries) Entries = std::make_unique(); Entries->push_back(getReader().createOperation(Opcode, Operands)); } // 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) { if (dwarf::DW_OP_fbreg == Entries->front()->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(this), StringRef(Stream.str()), /*UseQuotes=*/false, /*PrintRef=*/false); } }