
This adds an option --show-tags to "memory read". (lldb) memory read mte_buf mte_buf+32 -f "x" -s8 --show-tags 0x900fffff7ff8000: 0x0000000000000000 0x0000000000000000 (tag: 0x0) 0x900fffff7ff8010: 0x0000000000000000 0x0000000000000000 (tag: 0x1) Tags are printed on the end of each line, if that line has any tags associated with it. Meaning that untagged memory output is unchanged. Tags are printed based on the granule(s) of memory that a line covers. So you may have lines with 1 tag, with many tags, no tags or partially tagged lines. In the case of partially tagged lines, untagged granules will show "<no tag>" so that the ordering is obvious. For example, a line that covers 2 granules where the first is not tagged: (lldb) memory read mte_buf-16 mte_buf+16 -l32 -f"x" --show-tags 0x900fffff7ff7ff0: 0x00000000 <...> (tags: <no tag> 0x0) Untagged lines will just not have the "(tags: ..." at all. Though they may be part of a larger output that does have some tagged lines. To do this I've extended DumpDataExtractor to also print memory tags where it has a valid execution context and is asked to print them. There are no special alignment requirements, simply use "memory read" as usual. All alignment is handled in DumpDataExtractor. We use MakeTaggedRanges to find all the tagged memory in the current dump, then read all that into a MemoryTagMap. The tag map is populated once in DumpDataExtractor and re-used for each subsequently printed line (or recursive call of DumpDataExtractor, which some formats do). Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D107140
936 lines
32 KiB
C++
936 lines
32 KiB
C++
//===-- DumpDataExtractor.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/DumpDataExtractor.h"
|
|
|
|
#include "lldb/lldb-defines.h"
|
|
#include "lldb/lldb-forward.h"
|
|
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/Disassembler.h"
|
|
#include "lldb/Core/ModuleList.h"
|
|
#include "lldb/Target/ABI.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/ExecutionContextScope.h"
|
|
#include "lldb/Target/MemoryRegionInfo.h"
|
|
#include "lldb/Target/MemoryTagManager.h"
|
|
#include "lldb/Target/MemoryTagMap.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/DataExtractor.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
|
|
#include <bitset>
|
|
#include <sstream>
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb;
|
|
|
|
#define NON_PRINTABLE_CHAR '.'
|
|
|
|
static float half2float(uint16_t half) {
|
|
union {
|
|
float f;
|
|
uint32_t u;
|
|
} u;
|
|
// Sign extend to 4 byte.
|
|
int32_t sign_extended = static_cast<int16_t>(half);
|
|
uint32_t v = static_cast<uint32_t>(sign_extended);
|
|
|
|
if (0 == (v & 0x7c00)) {
|
|
u.u = v & 0x80007FFFU;
|
|
return u.f * ldexpf(1, 125);
|
|
}
|
|
|
|
v <<= 13;
|
|
u.u = v | 0x70000000U;
|
|
return u.f * ldexpf(1, -112);
|
|
}
|
|
|
|
static llvm::Optional<llvm::APInt> GetAPInt(const DataExtractor &data,
|
|
lldb::offset_t *offset_ptr,
|
|
lldb::offset_t byte_size) {
|
|
if (byte_size == 0)
|
|
return llvm::None;
|
|
|
|
llvm::SmallVector<uint64_t, 2> uint64_array;
|
|
lldb::offset_t bytes_left = byte_size;
|
|
uint64_t u64;
|
|
const lldb::ByteOrder byte_order = data.GetByteOrder();
|
|
if (byte_order == lldb::eByteOrderLittle) {
|
|
while (bytes_left > 0) {
|
|
if (bytes_left >= 8) {
|
|
u64 = data.GetU64(offset_ptr);
|
|
bytes_left -= 8;
|
|
} else {
|
|
u64 = data.GetMaxU64(offset_ptr, (uint32_t)bytes_left);
|
|
bytes_left = 0;
|
|
}
|
|
uint64_array.push_back(u64);
|
|
}
|
|
return llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
|
|
} else if (byte_order == lldb::eByteOrderBig) {
|
|
lldb::offset_t be_offset = *offset_ptr + byte_size;
|
|
lldb::offset_t temp_offset;
|
|
while (bytes_left > 0) {
|
|
if (bytes_left >= 8) {
|
|
be_offset -= 8;
|
|
temp_offset = be_offset;
|
|
u64 = data.GetU64(&temp_offset);
|
|
bytes_left -= 8;
|
|
} else {
|
|
be_offset -= bytes_left;
|
|
temp_offset = be_offset;
|
|
u64 = data.GetMaxU64(&temp_offset, (uint32_t)bytes_left);
|
|
bytes_left = 0;
|
|
}
|
|
uint64_array.push_back(u64);
|
|
}
|
|
*offset_ptr += byte_size;
|
|
return llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
|
|
}
|
|
return llvm::None;
|
|
}
|
|
|
|
static lldb::offset_t DumpAPInt(Stream *s, const DataExtractor &data,
|
|
lldb::offset_t offset, lldb::offset_t byte_size,
|
|
bool is_signed, unsigned radix) {
|
|
llvm::Optional<llvm::APInt> apint = GetAPInt(data, &offset, byte_size);
|
|
if (apint.hasValue()) {
|
|
std::string apint_str = toString(apint.getValue(), radix, is_signed);
|
|
switch (radix) {
|
|
case 2:
|
|
s->Write("0b", 2);
|
|
break;
|
|
case 8:
|
|
s->Write("0", 1);
|
|
break;
|
|
case 10:
|
|
break;
|
|
}
|
|
s->Write(apint_str.c_str(), apint_str.size());
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
/// Dumps decoded instructions to a stream.
|
|
static lldb::offset_t DumpInstructions(const DataExtractor &DE, Stream *s,
|
|
ExecutionContextScope *exe_scope,
|
|
offset_t start_offset,
|
|
uint64_t base_addr,
|
|
size_t number_of_instructions) {
|
|
offset_t offset = start_offset;
|
|
|
|
TargetSP target_sp;
|
|
if (exe_scope)
|
|
target_sp = exe_scope->CalculateTarget();
|
|
if (target_sp) {
|
|
DisassemblerSP disassembler_sp(
|
|
Disassembler::FindPlugin(target_sp->GetArchitecture(),
|
|
target_sp->GetDisassemblyFlavor(), nullptr));
|
|
if (disassembler_sp) {
|
|
lldb::addr_t addr = base_addr + start_offset;
|
|
lldb_private::Address so_addr;
|
|
bool data_from_file = true;
|
|
if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
|
|
data_from_file = false;
|
|
} else {
|
|
if (target_sp->GetSectionLoadList().IsEmpty() ||
|
|
!target_sp->GetImages().ResolveFileAddress(addr, so_addr))
|
|
so_addr.SetRawAddress(addr);
|
|
}
|
|
|
|
size_t bytes_consumed = disassembler_sp->DecodeInstructions(
|
|
so_addr, DE, start_offset, number_of_instructions, false,
|
|
data_from_file);
|
|
|
|
if (bytes_consumed) {
|
|
offset += bytes_consumed;
|
|
const bool show_address = base_addr != LLDB_INVALID_ADDRESS;
|
|
const bool show_bytes = true;
|
|
ExecutionContext exe_ctx;
|
|
exe_scope->CalculateExecutionContext(exe_ctx);
|
|
disassembler_sp->GetInstructionList().Dump(s, show_address, show_bytes,
|
|
&exe_ctx);
|
|
}
|
|
}
|
|
} else
|
|
s->Printf("invalid target");
|
|
|
|
return offset;
|
|
}
|
|
|
|
/// Prints the specific escape sequence of the given character to the stream.
|
|
/// If the character doesn't have a known specific escape sequence (e.g., '\a',
|
|
/// '\n' but not generic escape sequences such as'\x12'), this function will
|
|
/// not modify the stream and return false.
|
|
static bool TryDumpSpecialEscapedChar(Stream &s, const char c) {
|
|
switch (c) {
|
|
case '\033':
|
|
// Common non-standard escape code for 'escape'.
|
|
s.Printf("\\e");
|
|
return true;
|
|
case '\a':
|
|
s.Printf("\\a");
|
|
return true;
|
|
case '\b':
|
|
s.Printf("\\b");
|
|
return true;
|
|
case '\f':
|
|
s.Printf("\\f");
|
|
return true;
|
|
case '\n':
|
|
s.Printf("\\n");
|
|
return true;
|
|
case '\r':
|
|
s.Printf("\\r");
|
|
return true;
|
|
case '\t':
|
|
s.Printf("\\t");
|
|
return true;
|
|
case '\v':
|
|
s.Printf("\\v");
|
|
return true;
|
|
case '\0':
|
|
s.Printf("\\0");
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Dump the character to a stream. A character that is not printable will be
|
|
/// represented by its escape sequence.
|
|
static void DumpCharacter(Stream &s, const char c) {
|
|
if (TryDumpSpecialEscapedChar(s, c))
|
|
return;
|
|
if (llvm::isPrint(c)) {
|
|
s.PutChar(c);
|
|
return;
|
|
}
|
|
s.Printf("\\x%2.2x", c);
|
|
}
|
|
|
|
/// Dump a floating point type.
|
|
template <typename FloatT>
|
|
void DumpFloatingPoint(std::ostringstream &ss, FloatT f) {
|
|
static_assert(std::is_floating_point<FloatT>::value,
|
|
"Only floating point types can be dumped.");
|
|
// NaN and Inf are potentially implementation defined and on Darwin it
|
|
// seems NaNs are printed without their sign. Manually implement dumping them
|
|
// here to avoid having to deal with platform differences.
|
|
if (std::isnan(f)) {
|
|
if (std::signbit(f))
|
|
ss << '-';
|
|
ss << "nan";
|
|
return;
|
|
}
|
|
if (std::isinf(f)) {
|
|
if (std::signbit(f))
|
|
ss << '-';
|
|
ss << "inf";
|
|
return;
|
|
}
|
|
ss << f;
|
|
}
|
|
|
|
static llvm::Optional<MemoryTagMap>
|
|
GetMemoryTags(lldb::addr_t addr, size_t length,
|
|
ExecutionContextScope *exe_scope) {
|
|
assert(addr != LLDB_INVALID_ADDRESS);
|
|
|
|
if (!exe_scope)
|
|
return llvm::None;
|
|
|
|
TargetSP target_sp = exe_scope->CalculateTarget();
|
|
if (!target_sp)
|
|
return llvm::None;
|
|
|
|
ProcessSP process_sp = target_sp->CalculateProcess();
|
|
if (!process_sp)
|
|
return llvm::None;
|
|
|
|
llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
|
|
process_sp->GetMemoryTagManager();
|
|
if (!tag_manager_or_err) {
|
|
llvm::consumeError(tag_manager_or_err.takeError());
|
|
return llvm::None;
|
|
}
|
|
|
|
MemoryRegionInfos memory_regions;
|
|
// Don't check return status, list will be just empty if an error happened.
|
|
process_sp->GetMemoryRegions(memory_regions);
|
|
|
|
llvm::Expected<std::vector<MemoryTagManager::TagRange>> tagged_ranges_or_err =
|
|
(*tag_manager_or_err)
|
|
->MakeTaggedRanges(addr, addr + length, memory_regions);
|
|
// Here we know that our range will not be inverted but we must still check
|
|
// for an error.
|
|
if (!tagged_ranges_or_err) {
|
|
llvm::consumeError(tagged_ranges_or_err.takeError());
|
|
return llvm::None;
|
|
}
|
|
if (tagged_ranges_or_err->empty())
|
|
return llvm::None;
|
|
|
|
MemoryTagMap memory_tag_map(*tag_manager_or_err);
|
|
for (const MemoryTagManager::TagRange &range : *tagged_ranges_or_err) {
|
|
llvm::Expected<std::vector<lldb::addr_t>> tags_or_err =
|
|
process_sp->ReadMemoryTags(range.GetRangeBase(), range.GetByteSize());
|
|
|
|
if (tags_or_err)
|
|
memory_tag_map.InsertTags(range.GetRangeBase(), *tags_or_err);
|
|
else
|
|
llvm::consumeError(tags_or_err.takeError());
|
|
}
|
|
|
|
if (memory_tag_map.Empty())
|
|
return llvm::None;
|
|
|
|
return memory_tag_map;
|
|
}
|
|
|
|
static void
|
|
printMemoryTags(const DataExtractor &DE, Stream *s, lldb::addr_t addr,
|
|
size_t len,
|
|
const llvm::Optional<MemoryTagMap> &memory_tag_map) {
|
|
std::vector<llvm::Optional<lldb::addr_t>> tags =
|
|
memory_tag_map->GetTags(addr, len);
|
|
|
|
// Only print if there is at least one tag for this line
|
|
if (tags.empty())
|
|
return;
|
|
|
|
s->Printf(" (tag%s:", tags.size() > 1 ? "s" : "");
|
|
// Some granules may not be tagged but print something for them
|
|
// so that the ordering remains intact.
|
|
for (auto tag : tags) {
|
|
if (tag)
|
|
s->Printf(" 0x%" PRIx64, *tag);
|
|
else
|
|
s->PutCString(" <no tag>");
|
|
}
|
|
s->PutCString(")");
|
|
}
|
|
|
|
lldb::offset_t lldb_private::DumpDataExtractor(
|
|
const DataExtractor &DE, Stream *s, offset_t start_offset,
|
|
lldb::Format item_format, size_t item_byte_size, size_t item_count,
|
|
size_t num_per_line, uint64_t base_addr,
|
|
uint32_t item_bit_size, // If zero, this is not a bitfield value, if
|
|
// non-zero, the value is a bitfield
|
|
uint32_t item_bit_offset, // If "item_bit_size" is non-zero, this is the
|
|
// shift amount to apply to a bitfield
|
|
ExecutionContextScope *exe_scope, bool show_memory_tags) {
|
|
if (s == nullptr)
|
|
return start_offset;
|
|
|
|
if (item_format == eFormatPointer) {
|
|
if (item_byte_size != 4 && item_byte_size != 8)
|
|
item_byte_size = s->GetAddressByteSize();
|
|
}
|
|
|
|
offset_t offset = start_offset;
|
|
|
|
llvm::Optional<MemoryTagMap> memory_tag_map = llvm::None;
|
|
if (show_memory_tags && base_addr != LLDB_INVALID_ADDRESS)
|
|
memory_tag_map =
|
|
GetMemoryTags(base_addr, DE.GetByteSize() - offset, exe_scope);
|
|
|
|
if (item_format == eFormatInstruction)
|
|
return DumpInstructions(DE, s, exe_scope, start_offset, base_addr,
|
|
item_count);
|
|
|
|
if ((item_format == eFormatOSType || item_format == eFormatAddressInfo) &&
|
|
item_byte_size > 8)
|
|
item_format = eFormatHex;
|
|
|
|
lldb::offset_t line_start_offset = start_offset;
|
|
for (uint32_t count = 0; DE.ValidOffset(offset) && count < item_count;
|
|
++count) {
|
|
// If we are at the beginning or end of a line
|
|
// Note that the last line is handled outside this for loop.
|
|
if ((count % num_per_line) == 0) {
|
|
// If we are at the end of a line
|
|
if (count > 0) {
|
|
if (item_format == eFormatBytesWithASCII &&
|
|
offset > line_start_offset) {
|
|
s->Printf("%*s",
|
|
static_cast<int>(
|
|
(num_per_line - (offset - line_start_offset)) * 3 + 2),
|
|
"");
|
|
DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
|
|
offset - line_start_offset, SIZE_MAX,
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
}
|
|
|
|
if (base_addr != LLDB_INVALID_ADDRESS && memory_tag_map) {
|
|
size_t line_len = offset - line_start_offset;
|
|
lldb::addr_t line_base =
|
|
base_addr +
|
|
(offset - start_offset - line_len) / DE.getTargetByteSize();
|
|
printMemoryTags(DE, s, line_base, line_len, memory_tag_map);
|
|
}
|
|
|
|
s->EOL();
|
|
}
|
|
if (base_addr != LLDB_INVALID_ADDRESS)
|
|
s->Printf("0x%8.8" PRIx64 ": ",
|
|
(uint64_t)(base_addr +
|
|
(offset - start_offset) / DE.getTargetByteSize()));
|
|
|
|
line_start_offset = offset;
|
|
} else if (item_format != eFormatChar &&
|
|
item_format != eFormatCharPrintable &&
|
|
item_format != eFormatCharArray && count > 0) {
|
|
s->PutChar(' ');
|
|
}
|
|
|
|
switch (item_format) {
|
|
case eFormatBoolean:
|
|
if (item_byte_size <= 8)
|
|
s->Printf("%s", DE.GetMaxU64Bitfield(&offset, item_byte_size,
|
|
item_bit_size, item_bit_offset)
|
|
? "true"
|
|
: "false");
|
|
else {
|
|
s->Printf("error: unsupported byte size (%" PRIu64
|
|
") for boolean format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
break;
|
|
|
|
case eFormatBinary:
|
|
if (item_byte_size <= 8) {
|
|
uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
|
|
item_bit_size, item_bit_offset);
|
|
// Avoid std::bitset<64>::to_string() since it is missing in earlier
|
|
// C++ libraries
|
|
std::string binary_value(64, '0');
|
|
std::bitset<64> bits(uval64);
|
|
for (uint32_t i = 0; i < 64; ++i)
|
|
if (bits[i])
|
|
binary_value[64 - 1 - i] = '1';
|
|
if (item_bit_size > 0)
|
|
s->Printf("0b%s", binary_value.c_str() + 64 - item_bit_size);
|
|
else if (item_byte_size > 0 && item_byte_size <= 8)
|
|
s->Printf("0b%s", binary_value.c_str() + 64 - item_byte_size * 8);
|
|
} else {
|
|
const bool is_signed = false;
|
|
const unsigned radix = 2;
|
|
offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
|
|
}
|
|
break;
|
|
|
|
case eFormatBytes:
|
|
case eFormatBytesWithASCII:
|
|
for (uint32_t i = 0; i < item_byte_size; ++i) {
|
|
s->Printf("%2.2x", DE.GetU8(&offset));
|
|
}
|
|
|
|
// Put an extra space between the groups of bytes if more than one is
|
|
// being dumped in a group (item_byte_size is more than 1).
|
|
if (item_byte_size > 1)
|
|
s->PutChar(' ');
|
|
break;
|
|
|
|
case eFormatChar:
|
|
case eFormatCharPrintable:
|
|
case eFormatCharArray: {
|
|
// Reject invalid item_byte_size.
|
|
if (item_byte_size > 8) {
|
|
s->Printf("error: unsupported byte size (%" PRIu64 ") for char format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
|
|
// If we are only printing one character surround it with single quotes
|
|
if (item_count == 1 && item_format == eFormatChar)
|
|
s->PutChar('\'');
|
|
|
|
const uint64_t ch = DE.GetMaxU64Bitfield(&offset, item_byte_size,
|
|
item_bit_size, item_bit_offset);
|
|
if (llvm::isPrint(ch))
|
|
s->Printf("%c", (char)ch);
|
|
else if (item_format != eFormatCharPrintable) {
|
|
if (!TryDumpSpecialEscapedChar(*s, ch)) {
|
|
if (item_byte_size == 1)
|
|
s->Printf("\\x%2.2x", (uint8_t)ch);
|
|
else
|
|
s->Printf("%" PRIu64, ch);
|
|
}
|
|
} else {
|
|
s->PutChar(NON_PRINTABLE_CHAR);
|
|
}
|
|
|
|
// If we are only printing one character surround it with single quotes
|
|
if (item_count == 1 && item_format == eFormatChar)
|
|
s->PutChar('\'');
|
|
} break;
|
|
|
|
case eFormatEnum: // Print enum value as a signed integer when we don't get
|
|
// the enum type
|
|
case eFormatDecimal:
|
|
if (item_byte_size <= 8)
|
|
s->Printf("%" PRId64,
|
|
DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset));
|
|
else {
|
|
const bool is_signed = true;
|
|
const unsigned radix = 10;
|
|
offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
|
|
}
|
|
break;
|
|
|
|
case eFormatUnsigned:
|
|
if (item_byte_size <= 8)
|
|
s->Printf("%" PRIu64,
|
|
DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset));
|
|
else {
|
|
const bool is_signed = false;
|
|
const unsigned radix = 10;
|
|
offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
|
|
}
|
|
break;
|
|
|
|
case eFormatOctal:
|
|
if (item_byte_size <= 8)
|
|
s->Printf("0%" PRIo64,
|
|
DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset));
|
|
else {
|
|
const bool is_signed = false;
|
|
const unsigned radix = 8;
|
|
offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
|
|
}
|
|
break;
|
|
|
|
case eFormatOSType: {
|
|
uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
|
|
item_bit_size, item_bit_offset);
|
|
s->PutChar('\'');
|
|
for (uint32_t i = 0; i < item_byte_size; ++i) {
|
|
uint8_t ch = (uint8_t)(uval64 >> ((item_byte_size - i - 1) * 8));
|
|
DumpCharacter(*s, ch);
|
|
}
|
|
s->PutChar('\'');
|
|
} break;
|
|
|
|
case eFormatCString: {
|
|
const char *cstr = DE.GetCStr(&offset);
|
|
|
|
if (!cstr) {
|
|
s->Printf("NULL");
|
|
offset = LLDB_INVALID_OFFSET;
|
|
} else {
|
|
s->PutChar('\"');
|
|
|
|
while (const char c = *cstr) {
|
|
DumpCharacter(*s, c);
|
|
++cstr;
|
|
}
|
|
|
|
s->PutChar('\"');
|
|
}
|
|
} break;
|
|
|
|
case eFormatPointer:
|
|
DumpAddress(s->AsRawOstream(),
|
|
DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset),
|
|
sizeof(addr_t));
|
|
break;
|
|
|
|
case eFormatComplexInteger: {
|
|
size_t complex_int_byte_size = item_byte_size / 2;
|
|
|
|
if (complex_int_byte_size > 0 && complex_int_byte_size <= 8) {
|
|
s->Printf("%" PRIu64,
|
|
DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
|
|
s->Printf(" + %" PRIu64 "i",
|
|
DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
|
|
} else {
|
|
s->Printf("error: unsupported byte size (%" PRIu64
|
|
") for complex integer format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
} break;
|
|
|
|
case eFormatComplex:
|
|
if (sizeof(float) * 2 == item_byte_size) {
|
|
float f32_1 = DE.GetFloat(&offset);
|
|
float f32_2 = DE.GetFloat(&offset);
|
|
|
|
s->Printf("%g + %gi", f32_1, f32_2);
|
|
break;
|
|
} else if (sizeof(double) * 2 == item_byte_size) {
|
|
double d64_1 = DE.GetDouble(&offset);
|
|
double d64_2 = DE.GetDouble(&offset);
|
|
|
|
s->Printf("%lg + %lgi", d64_1, d64_2);
|
|
break;
|
|
} else if (sizeof(long double) * 2 == item_byte_size) {
|
|
long double ld64_1 = DE.GetLongDouble(&offset);
|
|
long double ld64_2 = DE.GetLongDouble(&offset);
|
|
s->Printf("%Lg + %Lgi", ld64_1, ld64_2);
|
|
break;
|
|
} else {
|
|
s->Printf("error: unsupported byte size (%" PRIu64
|
|
") for complex float format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
case eFormatDefault:
|
|
case eFormatHex:
|
|
case eFormatHexUppercase: {
|
|
bool wantsuppercase = (item_format == eFormatHexUppercase);
|
|
switch (item_byte_size) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64,
|
|
(int)(2 * item_byte_size), (int)(2 * item_byte_size),
|
|
DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset));
|
|
break;
|
|
default: {
|
|
assert(item_bit_size == 0 && item_bit_offset == 0);
|
|
const uint8_t *bytes =
|
|
(const uint8_t *)DE.GetData(&offset, item_byte_size);
|
|
if (bytes) {
|
|
s->PutCString("0x");
|
|
uint32_t idx;
|
|
if (DE.GetByteOrder() == eByteOrderBig) {
|
|
for (idx = 0; idx < item_byte_size; ++idx)
|
|
s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]);
|
|
} else {
|
|
for (idx = 0; idx < item_byte_size; ++idx)
|
|
s->Printf(wantsuppercase ? "%2.2X" : "%2.2x",
|
|
bytes[item_byte_size - 1 - idx]);
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
} break;
|
|
|
|
case eFormatFloat: {
|
|
TargetSP target_sp;
|
|
bool used_upfloat = false;
|
|
if (exe_scope)
|
|
target_sp = exe_scope->CalculateTarget();
|
|
if (target_sp) {
|
|
auto type_system_or_err =
|
|
target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC);
|
|
if (!type_system_or_err) {
|
|
llvm::consumeError(type_system_or_err.takeError());
|
|
} else {
|
|
auto &type_system = *type_system_or_err;
|
|
llvm::SmallVector<char, 256> sv;
|
|
// Show full precision when printing float values
|
|
const unsigned format_precision = 0;
|
|
const unsigned format_max_padding =
|
|
target_sp->GetMaxZeroPaddingInFloatFormat();
|
|
|
|
const auto &semantics =
|
|
type_system.GetFloatTypeSemantics(item_byte_size);
|
|
|
|
// Recalculate the byte size in case of a difference. This is possible
|
|
// when item_byte_size is 16 (128-bit), because you could get back the
|
|
// x87DoubleExtended semantics which has a byte size of 10 (80-bit).
|
|
const size_t semantics_byte_size =
|
|
(llvm::APFloat::getSizeInBits(semantics) + 7) / 8;
|
|
llvm::Optional<llvm::APInt> apint =
|
|
GetAPInt(DE, &offset, semantics_byte_size);
|
|
if (apint.hasValue()) {
|
|
llvm::APFloat apfloat(semantics, apint.getValue());
|
|
apfloat.toString(sv, format_precision, format_max_padding);
|
|
if (!sv.empty()) {
|
|
s->Printf("%*.*s", (int)sv.size(), (int)sv.size(), sv.data());
|
|
used_upfloat = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!used_upfloat) {
|
|
std::ostringstream ss;
|
|
if (item_byte_size == sizeof(float) || item_byte_size == 2) {
|
|
float f;
|
|
if (item_byte_size == 2) {
|
|
uint16_t half = DE.GetU16(&offset);
|
|
f = half2float(half);
|
|
} else {
|
|
f = DE.GetFloat(&offset);
|
|
}
|
|
ss.precision(std::numeric_limits<float>::digits10);
|
|
DumpFloatingPoint(ss, f);
|
|
} else if (item_byte_size == sizeof(double)) {
|
|
ss.precision(std::numeric_limits<double>::digits10);
|
|
DumpFloatingPoint(ss, DE.GetDouble(&offset));
|
|
} else if (item_byte_size == sizeof(long double) ||
|
|
item_byte_size == 10) {
|
|
ss.precision(std::numeric_limits<long double>::digits10);
|
|
DumpFloatingPoint(ss, DE.GetLongDouble(&offset));
|
|
} else {
|
|
s->Printf("error: unsupported byte size (%" PRIu64
|
|
") for float format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
ss.flush();
|
|
s->Printf("%s", ss.str().c_str());
|
|
}
|
|
} break;
|
|
|
|
case eFormatUnicode16:
|
|
s->Printf("U+%4.4x", DE.GetU16(&offset));
|
|
break;
|
|
|
|
case eFormatUnicode32:
|
|
s->Printf("U+0x%8.8x", DE.GetU32(&offset));
|
|
break;
|
|
|
|
case eFormatAddressInfo: {
|
|
addr_t addr = DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
|
|
item_bit_offset);
|
|
s->Printf("0x%*.*" PRIx64, (int)(2 * item_byte_size),
|
|
(int)(2 * item_byte_size), addr);
|
|
if (exe_scope) {
|
|
TargetSP target_sp(exe_scope->CalculateTarget());
|
|
lldb_private::Address so_addr;
|
|
if (target_sp) {
|
|
if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr,
|
|
so_addr)) {
|
|
s->PutChar(' ');
|
|
so_addr.Dump(s, exe_scope, Address::DumpStyleResolvedDescription,
|
|
Address::DumpStyleModuleWithFileAddress);
|
|
} else {
|
|
so_addr.SetOffset(addr);
|
|
so_addr.Dump(s, exe_scope,
|
|
Address::DumpStyleResolvedPointerDescription);
|
|
if (ProcessSP process_sp = exe_scope->CalculateProcess()) {
|
|
if (ABISP abi_sp = process_sp->GetABI()) {
|
|
addr_t addr_fixed = abi_sp->FixCodeAddress(addr);
|
|
if (target_sp->GetSectionLoadList().ResolveLoadAddress(
|
|
addr_fixed, so_addr)) {
|
|
s->PutChar(' ');
|
|
s->Printf("(0x%*.*" PRIx64 ")", (int)(2 * item_byte_size),
|
|
(int)(2 * item_byte_size), addr_fixed);
|
|
s->PutChar(' ');
|
|
so_addr.Dump(s, exe_scope,
|
|
Address::DumpStyleResolvedDescription,
|
|
Address::DumpStyleModuleWithFileAddress);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case eFormatHexFloat:
|
|
if (sizeof(float) == item_byte_size) {
|
|
char float_cstr[256];
|
|
llvm::APFloat ap_float(DE.GetFloat(&offset));
|
|
ap_float.convertToHexString(float_cstr, 0, false,
|
|
llvm::APFloat::rmNearestTiesToEven);
|
|
s->Printf("%s", float_cstr);
|
|
break;
|
|
} else if (sizeof(double) == item_byte_size) {
|
|
char float_cstr[256];
|
|
llvm::APFloat ap_float(DE.GetDouble(&offset));
|
|
ap_float.convertToHexString(float_cstr, 0, false,
|
|
llvm::APFloat::rmNearestTiesToEven);
|
|
s->Printf("%s", float_cstr);
|
|
break;
|
|
} else {
|
|
s->Printf("error: unsupported byte size (%" PRIu64
|
|
") for hex float format",
|
|
(uint64_t)item_byte_size);
|
|
return offset;
|
|
}
|
|
break;
|
|
|
|
// please keep the single-item formats below in sync with
|
|
// FormatManager::GetSingleItemFormat if you fail to do so, users will
|
|
// start getting different outputs depending on internal implementation
|
|
// details they should not care about ||
|
|
case eFormatVectorOfChar: // ||
|
|
s->PutChar('{'); // \/
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatCharArray, 1, item_byte_size,
|
|
item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfSInt8:
|
|
s->PutChar('{');
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatDecimal, 1, item_byte_size,
|
|
item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfUInt8:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(DE, s, offset, eFormatHex, 1, item_byte_size,
|
|
item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfSInt16:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(
|
|
DE, s, offset, eFormatDecimal, sizeof(uint16_t),
|
|
item_byte_size / sizeof(uint16_t), item_byte_size / sizeof(uint16_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfUInt16:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint16_t),
|
|
item_byte_size / sizeof(uint16_t),
|
|
item_byte_size / sizeof(uint16_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfSInt32:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(
|
|
DE, s, offset, eFormatDecimal, sizeof(uint32_t),
|
|
item_byte_size / sizeof(uint32_t), item_byte_size / sizeof(uint32_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfUInt32:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint32_t),
|
|
item_byte_size / sizeof(uint32_t),
|
|
item_byte_size / sizeof(uint32_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfSInt64:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(
|
|
DE, s, offset, eFormatDecimal, sizeof(uint64_t),
|
|
item_byte_size / sizeof(uint64_t), item_byte_size / sizeof(uint64_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfUInt64:
|
|
s->PutChar('{');
|
|
offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint64_t),
|
|
item_byte_size / sizeof(uint64_t),
|
|
item_byte_size / sizeof(uint64_t),
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfFloat16:
|
|
s->PutChar('{');
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatFloat, 2, item_byte_size / 2,
|
|
item_byte_size / 2, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfFloat32:
|
|
s->PutChar('{');
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatFloat, 4, item_byte_size / 4,
|
|
item_byte_size / 4, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfFloat64:
|
|
s->PutChar('{');
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatFloat, 8, item_byte_size / 8,
|
|
item_byte_size / 8, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
|
|
case eFormatVectorOfUInt128:
|
|
s->PutChar('{');
|
|
offset =
|
|
DumpDataExtractor(DE, s, offset, eFormatHex, 16, item_byte_size / 16,
|
|
item_byte_size / 16, LLDB_INVALID_ADDRESS, 0, 0);
|
|
s->PutChar('}');
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If anything was printed we want to catch the end of the last line.
|
|
// Since we will exit the for loop above before we get a chance to append to
|
|
// it normally.
|
|
if (offset > line_start_offset) {
|
|
if (item_format == eFormatBytesWithASCII) {
|
|
s->Printf("%*s",
|
|
static_cast<int>(
|
|
(num_per_line - (offset - line_start_offset)) * 3 + 2),
|
|
"");
|
|
DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
|
|
offset - line_start_offset, SIZE_MAX,
|
|
LLDB_INVALID_ADDRESS, 0, 0);
|
|
}
|
|
|
|
if (base_addr != LLDB_INVALID_ADDRESS && memory_tag_map) {
|
|
size_t line_len = offset - line_start_offset;
|
|
lldb::addr_t line_base = base_addr + (offset - start_offset - line_len) /
|
|
DE.getTargetByteSize();
|
|
printMemoryTags(DE, s, line_base, line_len, memory_tag_map);
|
|
}
|
|
}
|
|
|
|
return offset; // Return the offset at which we ended up
|
|
}
|
|
|
|
void lldb_private::DumpHexBytes(Stream *s, const void *src, size_t src_len,
|
|
uint32_t bytes_per_line,
|
|
lldb::addr_t base_addr) {
|
|
DataExtractor data(src, src_len, lldb::eByteOrderLittle, 4);
|
|
DumpDataExtractor(data, s,
|
|
0, // Offset into "src"
|
|
lldb::eFormatBytes, // Dump as hex bytes
|
|
1, // Size of each item is 1 for single bytes
|
|
src_len, // Number of bytes
|
|
bytes_per_line, // Num bytes per line
|
|
base_addr, // Base address
|
|
0, 0); // Bitfield info
|
|
}
|