| //===-- 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 << formatv("lit{0}", 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 << formatv("breg{0}+{1}{2}", Code - dwarf::DW_OP_breg0, | |
| Operands[0], RegisterName); | |
| 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 << formatv("reg{0}{1}", Code - dwarf::DW_OP_reg0, RegisterName); | |
| return; | |
| } | |
| Stream << formatv("#{0:x2} ", 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 << formatv("bregx {0}{1}+{2}", Operands[0], RegisterName, | |
| unsigned(Operands[1])); | |
| break; | |
| } | |
| case dwarf::DW_OP_regval_type: { | |
| std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); | |
| Stream << formatv("regval_type {0}{1}+{2}", Operands[0], RegisterName, | |
| 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"; | |
| 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"; | |
| 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: [Register, Offset, OffsetInUdt]. | |
| case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL_INDIR: | |
| Stream << "register_rel_indir " | |
| << getReader().getRegisterName(Opcode, Operands) << " offset " | |
| << int(Operands[1]) << " offset_in_udt " << int(Operands[2]); | |
| 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 << formatv("#{0:x2}: ", 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<LVUnsigned> Operands) { | |
| if (!Entries) | |
| Entries = std::make_unique<LVOperations>(); | |
| 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 LVLocation::printCommon(raw_ostream &OS, bool Full) const { | |
| print(OS, Full); | |
| printExtra(OS, Full); | |
| } | |
| 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 << formatv("{0:f2}%", Percentage); | |
| if (!Location->getIsLocationSimple()) | |
| Stream << formatv(" ({0}/{1})", 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); | |
| } | |
| } |