diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Utility')
62 files changed, 14655 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h new file mode 100644 index 000000000000..ed8ff722088d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h @@ -0,0 +1,151 @@ +//===-- ARM64_DWARF_Registers.h ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_ARM64_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_ARM64_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace arm64_dwarf { + +enum { + x0 = 0, + x1, + x2, + x3, + x4, + x5, + x6, + x7, + x8, + x9, + x10, + x11, + x12, + x13, + x14, + x15, + x16, + x17, + x18, + x19, + x20, + x21, + x22, + x23, + x24, + x25, + x26, + x27, + x28, + x29 = 29, + fp = x29, + x30 = 30, + lr = x30, + x31 = 31, + sp = x31, + pc = 32, + cpsr = 33, + // 34-45 reserved + + // 64-bit SVE Vector granule pseudo register + vg = 46, + + // VG ́8-bit SVE first fault register + ffr = 47, + + // VG x ́8-bit SVE predicate registers + p0 = 48, + p1, + p2, + p3, + p4, + p5, + p6, + p7, + p8, + p9, + p10, + p11, + p12, + p13, + p14, + p15, + + // V0-V31 (128 bit vector registers) + v0 = 64, + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8, + v9, + v10, + v11, + v12, + v13, + v14, + v15, + v16, + v17, + v18, + v19, + v20, + v21, + v22, + v23, + v24, + v25, + v26, + v27, + v28, + v29, + v30, + v31, + + // VG ́64-bit SVE vector registers + z0 = 96, + z1, + z2, + z3, + z4, + z5, + z6, + z7, + z8, + z9, + z10, + z11, + z12, + z13, + z14, + z15, + z16, + z17, + z18, + z19, + z20, + z21, + z22, + z23, + z24, + z25, + z26, + z27, + z28, + z29, + z30, + z31 +}; + +} // namespace arm64_dwarf + +#endif // LLDB_SOURCE_UTILITY_ARM64_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h new file mode 100644 index 000000000000..c235891ec015 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h @@ -0,0 +1,149 @@ +//===-- ARM64_ehframe_Registers.h -------------------------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_ARM64_EHFRAME_REGISTERS_H +#define LLDB_SOURCE_UTILITY_ARM64_EHFRAME_REGISTERS_H + +// The register numbers used in the eh_frame unwind information. +// Should be the same as DWARF register numbers. + +namespace arm64_ehframe { + +enum { + x0 = 0, + x1, + x2, + x3, + x4, + x5, + x6, + x7, + x8, + x9, + x10, + x11, + x12, + x13, + x14, + x15, + x16, + x17, + x18, + x19, + x20, + x21, + x22, + x23, + x24, + x25, + x26, + x27, + x28, + fp, // aka x29 + lr, // aka x30 + sp, // aka x31 aka wzr + pc, // value is 32 + cpsr, + // 34-45 reserved + + // 64-bit SVE Vector granule pseudo register + vg = 46, + + // VG ́8-bit SVE first fault register + ffr = 47, + + // VG x ́8-bit SVE predicate registers + p0 = 48, + p1, + p2, + p3, + p4, + p5, + p6, + p7, + p8, + p9, + p10, + p11, + p12, + p13, + p14, + p15, + + // V0-V31 (128 bit vector registers) + v0 = 64, + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8, + v9, + v10, + v11, + v12, + v13, + v14, + v15, + v16, + v17, + v18, + v19, + v20, + v21, + v22, + v23, + v24, + v25, + v26, + v27, + v28, + v29, + v30, + v31, + + // VG ́64-bit SVE vector registers + z0 = 96, + z1, + z2, + z3, + z4, + z5, + z6, + z7, + z8, + z9, + z10, + z11, + z12, + z13, + z14, + z15, + z16, + z17, + z18, + z19, + z20, + z21, + z22, + z23, + z24, + z25, + z26, + z27, + z28, + z29, + z30, + z31 +}; +} + +#endif // LLDB_SOURCE_UTILITY_ARM64_EHFRAME_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h new file mode 100644 index 000000000000..8a3018b27e7e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h @@ -0,0 +1,207 @@ +//===-- ARM_DWARF_Registers.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_ARM_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_ARM_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +enum { + dwarf_r0 = 0, + dwarf_r1, + dwarf_r2, + dwarf_r3, + dwarf_r4, + dwarf_r5, + dwarf_r6, + dwarf_r7, + dwarf_r8, + dwarf_r9, + dwarf_r10, + dwarf_r11, + dwarf_r12, + dwarf_sp, + dwarf_lr, + dwarf_pc, + dwarf_cpsr, + + dwarf_s0 = 64, + dwarf_s1, + dwarf_s2, + dwarf_s3, + dwarf_s4, + dwarf_s5, + dwarf_s6, + dwarf_s7, + dwarf_s8, + dwarf_s9, + dwarf_s10, + dwarf_s11, + dwarf_s12, + dwarf_s13, + dwarf_s14, + dwarf_s15, + dwarf_s16, + dwarf_s17, + dwarf_s18, + dwarf_s19, + dwarf_s20, + dwarf_s21, + dwarf_s22, + dwarf_s23, + dwarf_s24, + dwarf_s25, + dwarf_s26, + dwarf_s27, + dwarf_s28, + dwarf_s29, + dwarf_s30, + dwarf_s31, + + // FPA Registers 0-7 + dwarf_f0 = 96, + dwarf_f1, + dwarf_f2, + dwarf_f3, + dwarf_f4, + dwarf_f5, + dwarf_f6, + dwarf_f7, + + // Intel wireless MMX general purpose registers 0 - 7 + dwarf_wCGR0 = 104, + dwarf_wCGR1, + dwarf_wCGR2, + dwarf_wCGR3, + dwarf_wCGR4, + dwarf_wCGR5, + dwarf_wCGR6, + dwarf_wCGR7, + + // XScale accumulator register 0 - 7 (they do overlap with wCGR0 - wCGR7) + dwarf_ACC0 = 104, + dwarf_ACC1, + dwarf_ACC2, + dwarf_ACC3, + dwarf_ACC4, + dwarf_ACC5, + dwarf_ACC6, + dwarf_ACC7, + + // Intel wireless MMX data registers 0 - 15 + dwarf_wR0 = 112, + dwarf_wR1, + dwarf_wR2, + dwarf_wR3, + dwarf_wR4, + dwarf_wR5, + dwarf_wR6, + dwarf_wR7, + dwarf_wR8, + dwarf_wR9, + dwarf_wR10, + dwarf_wR11, + dwarf_wR12, + dwarf_wR13, + dwarf_wR14, + dwarf_wR15, + + dwarf_spsr = 128, + dwarf_spsr_fiq, + dwarf_spsr_irq, + dwarf_spsr_abt, + dwarf_spsr_und, + dwarf_spsr_svc, + + dwarf_r8_usr = 144, + dwarf_r9_usr, + dwarf_r10_usr, + dwarf_r11_usr, + dwarf_r12_usr, + dwarf_r13_usr, + dwarf_r14_usr, + dwarf_r8_fiq, + dwarf_r9_fiq, + dwarf_r10_fiq, + dwarf_r11_fiq, + dwarf_r12_fiq, + dwarf_r13_fiq, + dwarf_r14_fiq, + dwarf_r13_irq, + dwarf_r14_irq, + dwarf_r13_abt, + dwarf_r14_abt, + dwarf_r13_und, + dwarf_r14_und, + dwarf_r13_svc, + dwarf_r14_svc, + + // Intel wireless MMX control register in co-processor 0 - 7 + dwarf_wC0 = 192, + dwarf_wC1, + dwarf_wC2, + dwarf_wC3, + dwarf_wC4, + dwarf_wC5, + dwarf_wC6, + dwarf_wC7, + + // VFP-v3/Neon + dwarf_d0 = 256, + dwarf_d1, + dwarf_d2, + dwarf_d3, + dwarf_d4, + dwarf_d5, + dwarf_d6, + dwarf_d7, + dwarf_d8, + dwarf_d9, + dwarf_d10, + dwarf_d11, + dwarf_d12, + dwarf_d13, + dwarf_d14, + dwarf_d15, + dwarf_d16, + dwarf_d17, + dwarf_d18, + dwarf_d19, + dwarf_d20, + dwarf_d21, + dwarf_d22, + dwarf_d23, + dwarf_d24, + dwarf_d25, + dwarf_d26, + dwarf_d27, + dwarf_d28, + dwarf_d29, + dwarf_d30, + dwarf_d31, + + // Neon quadword registers + dwarf_q0 = 288, + dwarf_q1, + dwarf_q2, + dwarf_q3, + dwarf_q4, + dwarf_q5, + dwarf_q6, + dwarf_q7, + dwarf_q8, + dwarf_q9, + dwarf_q10, + dwarf_q11, + dwarf_q12, + dwarf_q13, + dwarf_q14, + dwarf_q15 +}; + +#endif // LLDB_SOURCE_UTILITY_ARM_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h new file mode 100644 index 000000000000..c64e1db0c3cd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h @@ -0,0 +1,36 @@ +//===-- ARM_ehframe_Registers.h -------------------------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_ARM_EHFRAME_REGISTERS_H +#define LLDB_SOURCE_UTILITY_ARM_EHFRAME_REGISTERS_H + +// The register numbers used in the eh_frame unwind information. +// Should be the same as DWARF register numbers. + +enum { + ehframe_r0 = 0, + ehframe_r1, + ehframe_r2, + ehframe_r3, + ehframe_r4, + ehframe_r5, + ehframe_r6, + ehframe_r7, + ehframe_r8, + ehframe_r9, + ehframe_r10, + ehframe_r11, + ehframe_r12, + ehframe_sp, + ehframe_lr, + ehframe_pc, + ehframe_cpsr +}; + +#endif // LLDB_SOURCE_UTILITY_ARM_EHFRAME_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/AddressableBits.cpp b/contrib/llvm-project/lldb/source/Utility/AddressableBits.cpp new file mode 100644 index 000000000000..4c98addc1f07 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/AddressableBits.cpp @@ -0,0 +1,51 @@ +//===-- AddressableBits.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/Utility/AddressableBits.h" +#include "lldb/lldb-types.h" + +#include <cassert> + +using namespace lldb; +using namespace lldb_private; + +void AddressableBits::SetAddressableBits(uint32_t addressing_bits) { + m_low_memory_addr_bits = m_high_memory_addr_bits = addressing_bits; +} + +void AddressableBits::SetAddressableBits(uint32_t lowmem_addressing_bits, + uint32_t highmem_addressing_bits) { + m_low_memory_addr_bits = lowmem_addressing_bits; + m_high_memory_addr_bits = highmem_addressing_bits; +} + +void AddressableBits::SetLowmemAddressableBits( + uint32_t lowmem_addressing_bits) { + m_low_memory_addr_bits = lowmem_addressing_bits; +} + +uint32_t AddressableBits::GetLowmemAddressableBits() const { + return m_low_memory_addr_bits; +} + +void AddressableBits::SetHighmemAddressableBits( + uint32_t highmem_addressing_bits) { + m_high_memory_addr_bits = highmem_addressing_bits; +} + +uint32_t AddressableBits::GetHighmemAddressableBits() const { + return m_high_memory_addr_bits; +} + +addr_t AddressableBits::AddressableBitToMask(uint32_t addressable_bits) { + assert(addressable_bits <= sizeof(addr_t) * 8); + if (addressable_bits == 64) + return 0; // all bits used for addressing + else + return ~((1ULL << addressable_bits) - 1); +} diff --git a/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp b/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp new file mode 100644 index 000000000000..07ef435ef451 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp @@ -0,0 +1,1464 @@ +//===-- ArchSpec.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/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBLog.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StringList.h" +#include "lldb/lldb-defines.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Compiler.h" +#include "llvm/TargetParser/ARMTargetParser.h" + +using namespace lldb; +using namespace lldb_private; + +static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, + bool try_inverse, bool enforce_exact_match); + +namespace lldb_private { + +struct CoreDefinition { + ByteOrder default_byte_order; + uint32_t addr_byte_size; + uint32_t min_opcode_byte_size; + uint32_t max_opcode_byte_size; + llvm::Triple::ArchType machine; + ArchSpec::Core core; + const char *const name; +}; + +} // namespace lldb_private + +// This core information can be looked using the ArchSpec::Core as the index +static const CoreDefinition g_core_definitions[] = { + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_generic, + "arm"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4, + "armv4"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4t, + "armv4t"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5, + "armv5"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5e, + "armv5e"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5t, + "armv5t"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6, + "armv6"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6m, + "armv6m"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7, + "armv7"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7l, + "armv7l"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7f, + "armv7f"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7s, + "armv7s"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7k, + "armv7k"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7m, + "armv7m"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7em, + "armv7em"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_xscale, + "xscale"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumb, + "thumb"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv4t, + "thumbv4t"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5, + "thumbv5"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5e, + "thumbv5e"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6, + "thumbv6"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6m, + "thumbv6m"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7, + "thumbv7"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7f, + "thumbv7f"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7s, + "thumbv7s"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7k, + "thumbv7k"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7m, + "thumbv7m"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7em, + "thumbv7em"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, + ArchSpec::eCore_arm_arm64, "arm64"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, + ArchSpec::eCore_arm_armv8, "armv8"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv8l, + "armv8l"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, + ArchSpec::eCore_arm_arm64e, "arm64e"}, + {eByteOrderLittle, 4, 4, 4, llvm::Triple::aarch64_32, + ArchSpec::eCore_arm_arm64_32, "arm64_32"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, + ArchSpec::eCore_arm_aarch64, "aarch64"}, + + // mips32, mips32r2, mips32r3, mips32r5, mips32r6 + {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32, + "mips"}, + {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r2, + "mipsr2"}, + {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r3, + "mipsr3"}, + {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r5, + "mipsr5"}, + {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r6, + "mipsr6"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el, + "mipsel"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, + ArchSpec::eCore_mips32r2el, "mipsr2el"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, + ArchSpec::eCore_mips32r3el, "mipsr3el"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, + ArchSpec::eCore_mips32r5el, "mipsr5el"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, + ArchSpec::eCore_mips32r6el, "mipsr6el"}, + + // mips64, mips64r2, mips64r3, mips64r5, mips64r6 + {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64, + "mips64"}, + {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r2, + "mips64r2"}, + {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r3, + "mips64r3"}, + {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r5, + "mips64r5"}, + {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r6, + "mips64r6"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, + ArchSpec::eCore_mips64el, "mips64el"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, + ArchSpec::eCore_mips64r2el, "mips64r2el"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, + ArchSpec::eCore_mips64r3el, "mips64r3el"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, + ArchSpec::eCore_mips64r5el, "mips64r5el"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, + ArchSpec::eCore_mips64r6el, "mips64r6el"}, + + // MSP430 + {eByteOrderLittle, 2, 2, 4, llvm::Triple::msp430, ArchSpec::eCore_msp430, + "msp430"}, + + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_generic, + "powerpc"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc601, + "ppc601"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc602, + "ppc602"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603, + "ppc603"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603e, + "ppc603e"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603ev, + "ppc603ev"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604, + "ppc604"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604e, + "ppc604e"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc620, + "ppc620"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc750, + "ppc750"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7400, + "ppc7400"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7450, + "ppc7450"}, + {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc970, + "ppc970"}, + + {eByteOrderLittle, 8, 4, 4, llvm::Triple::ppc64le, + ArchSpec::eCore_ppc64le_generic, "powerpc64le"}, + {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_generic, + "powerpc64"}, + {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, + ArchSpec::eCore_ppc64_ppc970_64, "ppc970-64"}, + + {eByteOrderBig, 8, 2, 6, llvm::Triple::systemz, + ArchSpec::eCore_s390x_generic, "s390x"}, + + {eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc, + ArchSpec::eCore_sparc_generic, "sparc"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, + ArchSpec::eCore_sparc9_generic, "sparcv9"}, + + {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i386, + "i386"}, + {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486, + "i486"}, + {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, + ArchSpec::eCore_x86_32_i486sx, "i486sx"}, + {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i686, + "i686"}, + + {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, + ArchSpec::eCore_x86_64_x86_64, "x86_64"}, + {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, + ArchSpec::eCore_x86_64_x86_64h, "x86_64h"}, + {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, + ArchSpec::eCore_hexagon_generic, "hexagon"}, + {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, + ArchSpec::eCore_hexagon_hexagonv4, "hexagonv4"}, + {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, + ArchSpec::eCore_hexagon_hexagonv5, "hexagonv5"}, + + {eByteOrderLittle, 4, 2, 4, llvm::Triple::riscv32, ArchSpec::eCore_riscv32, + "riscv32"}, + {eByteOrderLittle, 8, 2, 4, llvm::Triple::riscv64, ArchSpec::eCore_riscv64, + "riscv64"}, + + {eByteOrderLittle, 4, 4, 4, llvm::Triple::loongarch32, + ArchSpec::eCore_loongarch32, "loongarch32"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::loongarch64, + ArchSpec::eCore_loongarch64, "loongarch64"}, + + {eByteOrderLittle, 4, 4, 4, llvm::Triple::UnknownArch, + ArchSpec::eCore_uknownMach32, "unknown-mach-32"}, + {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch, + ArchSpec::eCore_uknownMach64, "unknown-mach-64"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arc, ArchSpec::eCore_arc, "arc"}, + + {eByteOrderLittle, 2, 2, 4, llvm::Triple::avr, ArchSpec::eCore_avr, "avr"}, + + {eByteOrderLittle, 4, 1, 4, llvm::Triple::wasm32, ArchSpec::eCore_wasm32, + "wasm32"}, +}; + +// Ensure that we have an entry in the g_core_definitions for each core. If you +// comment out an entry above, you will need to comment out the corresponding +// ArchSpec::Core enumeration. +static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) == + ArchSpec::kNumCores, + "make sure we have one core definition for each core"); + +struct ArchDefinitionEntry { + ArchSpec::Core core; + uint32_t cpu; + uint32_t sub; + uint32_t cpu_mask; + uint32_t sub_mask; +}; + +struct ArchDefinition { + ArchitectureType type; + size_t num_entries; + const ArchDefinitionEntry *entries; + const char *name; +}; + +void ArchSpec::ListSupportedArchNames(StringList &list) { + for (const auto &def : g_core_definitions) + list.AppendString(def.name); +} + +void ArchSpec::AutoComplete(CompletionRequest &request) { + for (const auto &def : g_core_definitions) + request.TryCompleteCurrentArg(def.name); +} + +#define CPU_ANY (UINT32_MAX) + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +#define SUBTYPE_MASK 0x00FFFFFFu + +// clang-format off +static const ArchDefinitionEntry g_macho_arch_entries[] = { + {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, CPU_ANY, UINT32_MAX, UINT32_MAX}, + {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv4, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V4T, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv4t, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V4T, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv6, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V6, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv6m, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V6M, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv5, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv5e, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv5t, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_xscale, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_XSCALE, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7s, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7S, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7k, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7K, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7m, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7M, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_armv7em, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7EM, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64e, llvm::MachO::CPU_TYPE_ARM64, llvm::MachO::CPU_SUBTYPE_ARM64E, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, llvm::MachO::CPU_SUBTYPE_ARM64_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, llvm::MachO::CPU_SUBTYPE_ARM64_V8, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 13, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64_32, llvm::MachO::CPU_TYPE_ARM64_32, 0, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64_32, llvm::MachO::CPU_TYPE_ARM64_32, 1, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumb, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv4t, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V4T, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv5, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V5, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv5e, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V5, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv6, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V6, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv6m, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V6M, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7s, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7S, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7k, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7K, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7m, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7M, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_thumbv7em, llvm::MachO::CPU_TYPE_ARM, llvm::MachO::CPU_SUBTYPE_ARM_V7EM, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, CPU_ANY, UINT32_MAX, UINT32_MAX}, + {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc601, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_601, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc602, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_602, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc603, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_603, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc603e, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_603e, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc603ev, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_603ev, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc604, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_604, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc604e, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_604e, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc620, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_620, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc750, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_750, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc7400, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_7400, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc7450, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_7450, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc_ppc970, llvm::MachO::CPU_TYPE_POWERPC, llvm::MachO::CPU_SUBTYPE_POWERPC_970, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc64_generic, llvm::MachO::CPU_TYPE_POWERPC64, llvm::MachO::CPU_SUBTYPE_POWERPC_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc64le_generic, llvm::MachO::CPU_TYPE_POWERPC64, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_ppc64_ppc970_64, llvm::MachO::CPU_TYPE_POWERPC64, 100, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, llvm::MachO::CPU_SUBTYPE_I386_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_32_i486, llvm::MachO::CPU_TYPE_I386, llvm::MachO::CPU_SUBTYPE_486, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_32_i486sx, llvm::MachO::CPU_TYPE_I386, llvm::MachO::CPU_SUBTYPE_486SX, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, CPU_ANY, UINT32_MAX, UINT32_MAX}, + {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, llvm::MachO::CPU_SUBTYPE_X86_64_ALL, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, llvm::MachO::CPU_SUBTYPE_X86_ARCH1, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_64_x86_64h, llvm::MachO::CPU_TYPE_X86_64, llvm::MachO::CPU_SUBTYPE_X86_64_H, UINT32_MAX, SUBTYPE_MASK}, + {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, CPU_ANY, UINT32_MAX, UINT32_MAX}, + // Catch any unknown mach architectures so we can always use the object and symbol mach-o files + {ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u}, + {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}}; +// clang-format on + +static const ArchDefinition g_macho_arch_def = {eArchTypeMachO, + std::size(g_macho_arch_entries), + g_macho_arch_entries, "mach-o"}; + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +static const ArchDefinitionEntry g_elf_arch_entries[] = { + {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc + {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386 + {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct? + {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC + {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, + ArchSpec::eCore_ppc64le_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le + {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, + ArchSpec::eCore_ppc64_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64 + {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM + {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64 + {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ + {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9 + {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 + {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32 + {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2 + {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6 + {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el + {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el + {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el + {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64 + {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2 + {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6 + {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el + {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el + {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, + ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el + {ArchSpec::eCore_msp430, llvm::ELF::EM_MSP430, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // MSP430 + {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON + {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARC + {ArchSpec::eCore_avr, llvm::ELF::EM_AVR, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, + 0xFFFFFFFFu}, // AVR + {ArchSpec::eCore_riscv32, llvm::ELF::EM_RISCV, + ArchSpec::eRISCVSubType_riscv32, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv32 + {ArchSpec::eCore_riscv64, llvm::ELF::EM_RISCV, + ArchSpec::eRISCVSubType_riscv64, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv64 + {ArchSpec::eCore_loongarch32, llvm::ELF::EM_LOONGARCH, + ArchSpec::eLoongArchSubType_loongarch32, 0xFFFFFFFFu, + 0xFFFFFFFFu}, // loongarch32 + {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH, + ArchSpec::eLoongArchSubType_loongarch64, 0xFFFFFFFFu, + 0xFFFFFFFFu}, // loongarch64 +}; + +static const ArchDefinition g_elf_arch_def = { + eArchTypeELF, + std::size(g_elf_arch_entries), + g_elf_arch_entries, + "elf", +}; + +static const ArchDefinitionEntry g_coff_arch_entries[] = { + {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86 + {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC + {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU) + {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM + {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 + {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 + {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 + {ArchSpec::eCore_arm_arm64, llvm::COFF::IMAGE_FILE_MACHINE_ARM64, + LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // ARM64 +}; + +static const ArchDefinition g_coff_arch_def = { + eArchTypeCOFF, + std::size(g_coff_arch_entries), + g_coff_arch_entries, + "pe-coff", +}; + +//===----------------------------------------------------------------------===// +// Table of all ArchDefinitions +static const ArchDefinition *g_arch_definitions[] = { + &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def}; + +//===----------------------------------------------------------------------===// +// Static helper functions. + +// Get the architecture definition for a given object type. +static const ArchDefinition *FindArchDefinition(ArchitectureType arch_type) { + for (const ArchDefinition *def : g_arch_definitions) { + if (def->type == arch_type) + return def; + } + return nullptr; +} + +// Get an architecture definition by name. +static const CoreDefinition *FindCoreDefinition(llvm::StringRef name) { + for (const auto &def : g_core_definitions) { + if (name.equals_insensitive(def.name)) + return &def; + } + return nullptr; +} + +static inline const CoreDefinition *FindCoreDefinition(ArchSpec::Core core) { + if (core < std::size(g_core_definitions)) + return &g_core_definitions[core]; + return nullptr; +} + +// Get a definition entry by cpu type and subtype. +static const ArchDefinitionEntry * +FindArchDefinitionEntry(const ArchDefinition *def, uint32_t cpu, uint32_t sub) { + if (def == nullptr) + return nullptr; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) { + if (entries[i].cpu == (cpu & entries[i].cpu_mask)) + if (entries[i].sub == (sub & entries[i].sub_mask)) + return &entries[i]; + } + return nullptr; +} + +static const ArchDefinitionEntry * +FindArchDefinitionEntry(const ArchDefinition *def, ArchSpec::Core core) { + if (def == nullptr) + return nullptr; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) { + if (entries[i].core == core) + return &entries[i]; + } + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Constructors and destructors. + +ArchSpec::ArchSpec() = default; + +ArchSpec::ArchSpec(const char *triple_cstr) { + if (triple_cstr) + SetTriple(triple_cstr); +} + +ArchSpec::ArchSpec(llvm::StringRef triple_str) { SetTriple(triple_str); } + +ArchSpec::ArchSpec(const llvm::Triple &triple) { SetTriple(triple); } + +ArchSpec::ArchSpec(ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) { + SetArchitecture(arch_type, cpu, subtype); +} + +ArchSpec::~ArchSpec() = default; + +void ArchSpec::Clear() { + m_triple = llvm::Triple(); + m_core = kCore_invalid; + m_byte_order = eByteOrderInvalid; + m_flags = 0; +} + +//===----------------------------------------------------------------------===// +// Predicates. + +const char *ArchSpec::GetArchitectureName() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) + return core_def->name; + return "unknown"; +} + +bool ArchSpec::IsMIPS() const { return GetTriple().isMIPS(); } + +std::string ArchSpec::GetTargetABI() const { + + std::string abi; + + if (IsMIPS()) { + switch (GetFlags() & ArchSpec::eMIPSABI_mask) { + case ArchSpec::eMIPSABI_N64: + abi = "n64"; + return abi; + case ArchSpec::eMIPSABI_N32: + abi = "n32"; + return abi; + case ArchSpec::eMIPSABI_O32: + abi = "o32"; + return abi; + default: + return abi; + } + } + return abi; +} + +void ArchSpec::SetFlags(const std::string &elf_abi) { + + uint32_t flag = GetFlags(); + if (IsMIPS()) { + if (elf_abi == "n64") + flag |= ArchSpec::eMIPSABI_N64; + else if (elf_abi == "n32") + flag |= ArchSpec::eMIPSABI_N32; + else if (elf_abi == "o32") + flag |= ArchSpec::eMIPSABI_O32; + } + SetFlags(flag); +} + +std::string ArchSpec::GetClangTargetCPU() const { + std::string cpu; + if (IsMIPS()) { + switch (m_core) { + case ArchSpec::eCore_mips32: + case ArchSpec::eCore_mips32el: + cpu = "mips32"; + break; + case ArchSpec::eCore_mips32r2: + case ArchSpec::eCore_mips32r2el: + cpu = "mips32r2"; + break; + case ArchSpec::eCore_mips32r3: + case ArchSpec::eCore_mips32r3el: + cpu = "mips32r3"; + break; + case ArchSpec::eCore_mips32r5: + case ArchSpec::eCore_mips32r5el: + cpu = "mips32r5"; + break; + case ArchSpec::eCore_mips32r6: + case ArchSpec::eCore_mips32r6el: + cpu = "mips32r6"; + break; + case ArchSpec::eCore_mips64: + case ArchSpec::eCore_mips64el: + cpu = "mips64"; + break; + case ArchSpec::eCore_mips64r2: + case ArchSpec::eCore_mips64r2el: + cpu = "mips64r2"; + break; + case ArchSpec::eCore_mips64r3: + case ArchSpec::eCore_mips64r3el: + cpu = "mips64r3"; + break; + case ArchSpec::eCore_mips64r5: + case ArchSpec::eCore_mips64r5el: + cpu = "mips64r5"; + break; + case ArchSpec::eCore_mips64r6: + case ArchSpec::eCore_mips64r6el: + cpu = "mips64r6"; + break; + default: + break; + } + } + + if (GetTriple().isARM()) + cpu = llvm::ARM::getARMCPUForArch(GetTriple(), "").str(); + return cpu; +} + +uint32_t ArchSpec::GetMachOCPUType() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) { + const ArchDefinitionEntry *arch_def = + FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); + if (arch_def) { + return arch_def->cpu; + } + } + return LLDB_INVALID_CPUTYPE; +} + +uint32_t ArchSpec::GetMachOCPUSubType() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) { + const ArchDefinitionEntry *arch_def = + FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); + if (arch_def) { + return arch_def->sub; + } + } + return LLDB_INVALID_CPUTYPE; +} + +uint32_t ArchSpec::GetDataByteSize() const { + return 1; +} + +uint32_t ArchSpec::GetCodeByteSize() const { + return 1; +} + +llvm::Triple::ArchType ArchSpec::GetMachine() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) + return core_def->machine; + + return llvm::Triple::UnknownArch; +} + +uint32_t ArchSpec::GetAddressByteSize() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) { + if (core_def->machine == llvm::Triple::mips64 || + core_def->machine == llvm::Triple::mips64el) { + // For N32/O32 applications Address size is 4 bytes. + if (m_flags & (eMIPSABI_N32 | eMIPSABI_O32)) + return 4; + } + return core_def->addr_byte_size; + } + return 0; +} + +ByteOrder ArchSpec::GetDefaultEndian() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) + return core_def->default_byte_order; + return eByteOrderInvalid; +} + +bool ArchSpec::CharIsSignedByDefault() const { + switch (m_triple.getArch()) { + default: + return true; + + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + return m_triple.isOSDarwin() || m_triple.isOSWindows(); + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + return m_triple.isOSDarwin(); + + case llvm::Triple::ppc64le: + case llvm::Triple::systemz: + case llvm::Triple::xcore: + case llvm::Triple::arc: + return false; + } +} + +lldb::ByteOrder ArchSpec::GetByteOrder() const { + if (m_byte_order == eByteOrderInvalid) + return GetDefaultEndian(); + return m_byte_order; +} + +//===----------------------------------------------------------------------===// +// Mutators. + +bool ArchSpec::SetTriple(const llvm::Triple &triple) { + m_triple = triple; + UpdateCore(); + return IsValid(); +} + +bool lldb_private::ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str, + ArchSpec &arch) { + // Accept "12-10" or "12.10" as cpu type/subtype + if (triple_str.empty()) + return false; + + size_t pos = triple_str.find_first_of("-."); + if (pos == llvm::StringRef::npos) + return false; + + llvm::StringRef cpu_str = triple_str.substr(0, pos); + llvm::StringRef remainder = triple_str.substr(pos + 1); + if (cpu_str.empty() || remainder.empty()) + return false; + + llvm::StringRef sub_str; + llvm::StringRef vendor; + llvm::StringRef os; + std::tie(sub_str, remainder) = remainder.split('-'); + std::tie(vendor, os) = remainder.split('-'); + + uint32_t cpu = 0; + uint32_t sub = 0; + if (cpu_str.getAsInteger(10, cpu) || sub_str.getAsInteger(10, sub)) + return false; + + if (!arch.SetArchitecture(eArchTypeMachO, cpu, sub)) + return false; + if (!vendor.empty() && !os.empty()) { + arch.GetTriple().setVendorName(vendor); + arch.GetTriple().setOSName(os); + } + + return true; +} + +bool ArchSpec::SetTriple(llvm::StringRef triple) { + if (triple.empty()) { + Clear(); + return false; + } + + if (ParseMachCPUDashSubtypeTriple(triple, *this)) + return true; + + SetTriple(llvm::Triple(llvm::Triple::normalize(triple))); + return IsValid(); +} + +bool ArchSpec::ContainsOnlyArch(const llvm::Triple &normalized_triple) { + return !normalized_triple.getArchName().empty() && + normalized_triple.getOSName().empty() && + normalized_triple.getVendorName().empty() && + normalized_triple.getEnvironmentName().empty(); +} + +void ArchSpec::MergeFrom(const ArchSpec &other) { + // ios-macabi always wins over macosx. + if ((GetTriple().getOS() == llvm::Triple::MacOSX || + GetTriple().getOS() == llvm::Triple::UnknownOS) && + other.GetTriple().getOS() == llvm::Triple::IOS && + other.GetTriple().getEnvironment() == llvm::Triple::MacABI) { + (*this) = other; + return; + } + + if (!TripleVendorWasSpecified() && other.TripleVendorWasSpecified()) + GetTriple().setVendor(other.GetTriple().getVendor()); + if (!TripleOSWasSpecified() && other.TripleOSWasSpecified()) + GetTriple().setOS(other.GetTriple().getOS()); + if (GetTriple().getArch() == llvm::Triple::UnknownArch) { + GetTriple().setArch(other.GetTriple().getArch()); + + // MachO unknown64 isn't really invalid as the debugger can still obtain + // information from the binary, e.g. line tables. As such, we don't update + // the core here. + if (other.GetCore() != eCore_uknownMach64) + UpdateCore(); + } + if (!TripleEnvironmentWasSpecified() && + other.TripleEnvironmentWasSpecified()) { + GetTriple().setEnvironment(other.GetTriple().getEnvironment()); + } + // If this and other are both arm ArchSpecs and this ArchSpec is a generic + // "some kind of arm" spec but the other ArchSpec is a specific arm core, + // adopt the specific arm core. + if (GetTriple().getArch() == llvm::Triple::arm && + other.GetTriple().getArch() == llvm::Triple::arm && + IsCompatibleMatch(other) && GetCore() == ArchSpec::eCore_arm_generic && + other.GetCore() != ArchSpec::eCore_arm_generic) { + m_core = other.GetCore(); + CoreUpdated(false); + } + if (GetFlags() == 0) { + SetFlags(other.GetFlags()); + } +} + +bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu, + uint32_t sub, uint32_t os) { + m_core = kCore_invalid; + bool update_triple = true; + const ArchDefinition *arch_def = FindArchDefinition(arch_type); + if (arch_def) { + const ArchDefinitionEntry *arch_def_entry = + FindArchDefinitionEntry(arch_def, cpu, sub); + if (arch_def_entry) { + const CoreDefinition *core_def = FindCoreDefinition(arch_def_entry->core); + if (core_def) { + m_core = core_def->core; + update_triple = false; + // Always use the architecture name because it might be more + // descriptive than the architecture enum ("armv7" -> + // llvm::Triple::arm). + m_triple.setArchName(llvm::StringRef(core_def->name)); + if (arch_type == eArchTypeMachO) { + m_triple.setVendor(llvm::Triple::Apple); + + // Don't set the OS. It could be simulator, macosx, ios, watchos, + // tvos, bridgeos. We could get close with the cpu type - but we + // can't get it right all of the time. Better to leave this unset + // so other sections of code will set it when they have more + // information. NB: don't call m_triple.setOS + // (llvm::Triple::UnknownOS). That sets the OSName to "unknown" and + // the ArchSpec::TripleVendorWasSpecified() method says that any + // OSName setting means it was specified. + } else if (arch_type == eArchTypeELF) { + switch (os) { + case llvm::ELF::ELFOSABI_AIX: + m_triple.setOS(llvm::Triple::OSType::AIX); + break; + case llvm::ELF::ELFOSABI_FREEBSD: + m_triple.setOS(llvm::Triple::OSType::FreeBSD); + break; + case llvm::ELF::ELFOSABI_GNU: + m_triple.setOS(llvm::Triple::OSType::Linux); + break; + case llvm::ELF::ELFOSABI_NETBSD: + m_triple.setOS(llvm::Triple::OSType::NetBSD); + break; + case llvm::ELF::ELFOSABI_OPENBSD: + m_triple.setOS(llvm::Triple::OSType::OpenBSD); + break; + case llvm::ELF::ELFOSABI_SOLARIS: + m_triple.setOS(llvm::Triple::OSType::Solaris); + break; + case llvm::ELF::ELFOSABI_STANDALONE: + m_triple.setOS(llvm::Triple::OSType::UnknownOS); + break; + } + } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) { + m_triple.setVendor(llvm::Triple::PC); + m_triple.setOS(llvm::Triple::Win32); + } else { + m_triple.setVendor(llvm::Triple::UnknownVendor); + m_triple.setOS(llvm::Triple::UnknownOS); + } + // Fall back onto setting the machine type if the arch by name + // failed... + if (m_triple.getArch() == llvm::Triple::UnknownArch) + m_triple.setArch(core_def->machine); + } + } else { + Log *log(GetLog(LLDBLog::Target | LLDBLog::Process | LLDBLog::Platform)); + LLDB_LOGF(log, + "Unable to find a core definition for cpu 0x%" PRIx32 + " sub %" PRId32, + cpu, sub); + } + } + CoreUpdated(update_triple); + return IsValid(); +} + +uint32_t ArchSpec::GetMinimumOpcodeByteSize() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) + return core_def->min_opcode_byte_size; + return 0; +} + +uint32_t ArchSpec::GetMaximumOpcodeByteSize() const { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) + return core_def->max_opcode_byte_size; + return 0; +} + +static bool IsCompatibleEnvironment(llvm::Triple::EnvironmentType lhs, + llvm::Triple::EnvironmentType rhs) { + if (lhs == rhs) + return true; + + // Apple simulators are a different platform than what they simulate. + // As the environments are different at this point, if one of them is a + // simulator, then they are different. + if (lhs == llvm::Triple::Simulator || rhs == llvm::Triple::Simulator) + return false; + + // If any of the environment is unknown then they are compatible + if (lhs == llvm::Triple::UnknownEnvironment || + rhs == llvm::Triple::UnknownEnvironment) + return true; + + // If one of the environment is Android and the other one is EABI then they + // are considered to be compatible. This is required as a workaround for + // shared libraries compiled for Android without the NOTE section indicating + // that they are using the Android ABI. + if ((lhs == llvm::Triple::Android && rhs == llvm::Triple::EABI) || + (rhs == llvm::Triple::Android && lhs == llvm::Triple::EABI) || + (lhs == llvm::Triple::GNUEABI && rhs == llvm::Triple::EABI) || + (rhs == llvm::Triple::GNUEABI && lhs == llvm::Triple::EABI) || + (lhs == llvm::Triple::GNUEABIHF && rhs == llvm::Triple::EABIHF) || + (rhs == llvm::Triple::GNUEABIHF && lhs == llvm::Triple::EABIHF)) + return true; + + return false; +} + +bool ArchSpec::IsMatch(const ArchSpec &rhs, MatchType match) const { + if (GetByteOrder() != rhs.GetByteOrder() || + !cores_match(GetCore(), rhs.GetCore(), true, match == ExactMatch)) + return false; + + const llvm::Triple &lhs_triple = GetTriple(); + const llvm::Triple &rhs_triple = rhs.GetTriple(); + + const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); + const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); + + const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); + const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); + + bool both_windows = lhs_triple.isOSWindows() && rhs_triple.isOSWindows(); + + // On Windows, the vendor field doesn't have any practical effect, but + // it is often set to either "pc" or "w64". + if ((lhs_triple_vendor != rhs_triple_vendor) && + (match == ExactMatch || !both_windows)) { + const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); + const bool lhs_vendor_specified = TripleVendorWasSpecified(); + // Both architectures had the vendor specified, so if they aren't equal + // then we return false + if (rhs_vendor_specified && lhs_vendor_specified) + return false; + + // Only fail if both vendor types are not unknown + if (lhs_triple_vendor != llvm::Triple::UnknownVendor && + rhs_triple_vendor != llvm::Triple::UnknownVendor) + return false; + } + + const llvm::Triple::EnvironmentType lhs_triple_env = + lhs_triple.getEnvironment(); + const llvm::Triple::EnvironmentType rhs_triple_env = + rhs_triple.getEnvironment(); + + if (match == CompatibleMatch) { + // x86_64-apple-ios-macabi, x86_64-apple-macosx are compatible, no match. + if ((lhs_triple_os == llvm::Triple::IOS && + lhs_triple_env == llvm::Triple::MacABI && + rhs_triple_os == llvm::Triple::MacOSX) || + (lhs_triple_os == llvm::Triple::MacOSX && + rhs_triple_os == llvm::Triple::IOS && + rhs_triple_env == llvm::Triple::MacABI)) + return true; + } + + // x86_64-apple-ios-macabi and x86_64-apple-ios are not compatible. + if (lhs_triple_os == llvm::Triple::IOS && + rhs_triple_os == llvm::Triple::IOS && + (lhs_triple_env == llvm::Triple::MacABI || + rhs_triple_env == llvm::Triple::MacABI) && + lhs_triple_env != rhs_triple_env) + return false; + + if (lhs_triple_os != rhs_triple_os) { + const bool lhs_os_specified = TripleOSWasSpecified(); + const bool rhs_os_specified = rhs.TripleOSWasSpecified(); + // If both OS types are specified and different, fail. + if (lhs_os_specified && rhs_os_specified) + return false; + + // If the pair of os+env is both unspecified, match any other os+env combo. + if (match == CompatibleMatch && + ((!lhs_os_specified && !lhs_triple.hasEnvironment()) || + (!rhs_os_specified && !rhs_triple.hasEnvironment()))) + return true; + } + + if (match == CompatibleMatch && both_windows) + return true; // The Windows environments (MSVC vs GNU) are compatible + + return IsCompatibleEnvironment(lhs_triple_env, rhs_triple_env); +} + +void ArchSpec::UpdateCore() { + llvm::StringRef arch_name(m_triple.getArchName()); + const CoreDefinition *core_def = FindCoreDefinition(arch_name); + if (core_def) { + m_core = core_def->core; + // Set the byte order to the default byte order for an architecture. This + // can be modified if needed for cases when cores handle both big and + // little endian + m_byte_order = core_def->default_byte_order; + } else { + Clear(); + } +} + +//===----------------------------------------------------------------------===// +// Helper methods. + +void ArchSpec::CoreUpdated(bool update_triple) { + const CoreDefinition *core_def = FindCoreDefinition(m_core); + if (core_def) { + if (update_triple) + m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); + m_byte_order = core_def->default_byte_order; + } else { + if (update_triple) + m_triple = llvm::Triple(); + m_byte_order = eByteOrderInvalid; + } +} + +//===----------------------------------------------------------------------===// +// Operators. + +static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, + bool try_inverse, bool enforce_exact_match) { + if (core1 == core2) + return true; + + switch (core1) { + case ArchSpec::kCore_any: + return true; + + case ArchSpec::eCore_arm_generic: + if (enforce_exact_match) + break; + [[fallthrough]]; + case ArchSpec::kCore_arm_any: + if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) + return true; + if (core2 >= ArchSpec::kCore_thumb_first && + core2 <= ArchSpec::kCore_thumb_last) + return true; + if (core2 == ArchSpec::kCore_arm_any) + return true; + break; + + case ArchSpec::kCore_x86_32_any: + if ((core2 >= ArchSpec::kCore_x86_32_first && + core2 <= ArchSpec::kCore_x86_32_last) || + (core2 == ArchSpec::kCore_x86_32_any)) + return true; + break; + + case ArchSpec::kCore_x86_64_any: + if ((core2 >= ArchSpec::kCore_x86_64_first && + core2 <= ArchSpec::kCore_x86_64_last) || + (core2 == ArchSpec::kCore_x86_64_any)) + return true; + break; + + case ArchSpec::kCore_ppc_any: + if ((core2 >= ArchSpec::kCore_ppc_first && + core2 <= ArchSpec::kCore_ppc_last) || + (core2 == ArchSpec::kCore_ppc_any)) + return true; + break; + + case ArchSpec::kCore_ppc64_any: + if ((core2 >= ArchSpec::kCore_ppc64_first && + core2 <= ArchSpec::kCore_ppc64_last) || + (core2 == ArchSpec::kCore_ppc64_any)) + return true; + break; + + case ArchSpec::kCore_hexagon_any: + if ((core2 >= ArchSpec::kCore_hexagon_first && + core2 <= ArchSpec::kCore_hexagon_last) || + (core2 == ArchSpec::kCore_hexagon_any)) + return true; + break; + + // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M4 - ARMv7E-M - armv7em + case ArchSpec::eCore_arm_armv7em: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv7m) + return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + try_inverse = true; + } + break; + + // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M4 - ARMv7E-M - armv7em + case ArchSpec::eCore_arm_armv7m: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + if (core2 == ArchSpec::eCore_arm_armv7em) + return true; + try_inverse = true; + } + break; + + // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization + // Cortex-M0 - ARMv6-M - armv6m + // Cortex-M3 - ARMv7-M - armv7m + // Cortex-M4 - ARMv7E-M - armv7em + case ArchSpec::eCore_arm_armv6m: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv7em) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_arm_armv7f: + case ArchSpec::eCore_arm_armv7k: + case ArchSpec::eCore_arm_armv7s: + case ArchSpec::eCore_arm_armv7l: + case ArchSpec::eCore_arm_armv8l: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_x86_64_x86_64h: + if (!enforce_exact_match) { + try_inverse = false; + if (core2 == ArchSpec::eCore_x86_64_x86_64) + return true; + } + break; + + case ArchSpec::eCore_arm_armv8: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_arm64) + return true; + if (core2 == ArchSpec::eCore_arm_aarch64) + return true; + if (core2 == ArchSpec::eCore_arm_arm64e) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_arm_arm64e: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_arm64) + return true; + if (core2 == ArchSpec::eCore_arm_aarch64) + return true; + if (core2 == ArchSpec::eCore_arm_armv8) + return true; + try_inverse = false; + } + break; + case ArchSpec::eCore_arm_aarch64: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_arm64) + return true; + if (core2 == ArchSpec::eCore_arm_armv8) + return true; + if (core2 == ArchSpec::eCore_arm_arm64e) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_arm_arm64: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_aarch64) + return true; + if (core2 == ArchSpec::eCore_arm_armv8) + return true; + if (core2 == ArchSpec::eCore_arm_arm64e) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_arm_arm64_32: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips32: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32_first && + core2 <= ArchSpec::kCore_mips32_last) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips32el: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32el_first && + core2 <= ArchSpec::kCore_mips32el_last) + return true; + try_inverse = true; + } + break; + + case ArchSpec::eCore_mips64: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32_first && + core2 <= ArchSpec::kCore_mips32_last) + return true; + if (core2 >= ArchSpec::kCore_mips64_first && + core2 <= ArchSpec::kCore_mips64_last) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips64el: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32el_first && + core2 <= ArchSpec::kCore_mips32el_last) + return true; + if (core2 >= ArchSpec::kCore_mips64el_first && + core2 <= ArchSpec::kCore_mips64el_last) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips64r2: + case ArchSpec::eCore_mips64r3: + case ArchSpec::eCore_mips64r5: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32_first && core2 <= (core1 - 10)) + return true; + if (core2 >= ArchSpec::kCore_mips64_first && core2 <= (core1 - 1)) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips64r2el: + case ArchSpec::eCore_mips64r3el: + case ArchSpec::eCore_mips64r5el: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= (core1 - 10)) + return true; + if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= (core1 - 1)) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips32r2: + case ArchSpec::eCore_mips32r3: + case ArchSpec::eCore_mips32r5: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32_first && core2 <= core1) + return true; + } + break; + + case ArchSpec::eCore_mips32r2el: + case ArchSpec::eCore_mips32r3el: + case ArchSpec::eCore_mips32r5el: + if (!enforce_exact_match) { + if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= core1) + return true; + } + break; + + case ArchSpec::eCore_mips32r6: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) + return true; + } + break; + + case ArchSpec::eCore_mips32r6el: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_mips32el || + core2 == ArchSpec::eCore_mips32r6el) + return true; + } + break; + + case ArchSpec::eCore_mips64r6: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) + return true; + if (core2 == ArchSpec::eCore_mips64 || core2 == ArchSpec::eCore_mips64r6) + return true; + } + break; + + case ArchSpec::eCore_mips64r6el: + if (!enforce_exact_match) { + if (core2 == ArchSpec::eCore_mips32el || + core2 == ArchSpec::eCore_mips32r6el) + return true; + if (core2 == ArchSpec::eCore_mips64el || + core2 == ArchSpec::eCore_mips64r6el) + return true; + } + break; + + default: + break; + } + if (try_inverse) + return cores_match(core2, core1, false, enforce_exact_match); + return false; +} + +bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) { + const ArchSpec::Core lhs_core = lhs.GetCore(); + const ArchSpec::Core rhs_core = rhs.GetCore(); + return lhs_core < rhs_core; +} + + +bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) { + return lhs.GetCore() == rhs.GetCore(); +} + +bool ArchSpec::IsFullySpecifiedTriple() const { + if (!TripleOSWasSpecified()) + return false; + + if (!TripleVendorWasSpecified()) + return false; + + const unsigned unspecified = 0; + const llvm::Triple &triple = GetTriple(); + if (triple.isOSDarwin() && triple.getOSMajorVersion() == unspecified) + return false; + + return true; +} + +bool ArchSpec::IsAlwaysThumbInstructions() const { + std::string Status; + if (GetTriple().getArch() == llvm::Triple::arm || + GetTriple().getArch() == llvm::Triple::thumb) { + // v. https://en.wikipedia.org/wiki/ARM_Cortex-M + // + // Cortex-M0 through Cortex-M7 are ARM processor cores which can only + // execute thumb instructions. We map the cores to arch names like this: + // + // Cortex-M0, Cortex-M0+, Cortex-M1: armv6m Cortex-M3: armv7m Cortex-M4, + // Cortex-M7: armv7em + + if (GetCore() == ArchSpec::Core::eCore_arm_armv7m || + GetCore() == ArchSpec::Core::eCore_arm_armv7em || + GetCore() == ArchSpec::Core::eCore_arm_armv6m || + GetCore() == ArchSpec::Core::eCore_thumbv7m || + GetCore() == ArchSpec::Core::eCore_thumbv7em || + GetCore() == ArchSpec::Core::eCore_thumbv6m) { + return true; + } + // Windows on ARM is always thumb. + if (GetTriple().isOSWindows()) + return true; + } + return false; +} + +void ArchSpec::DumpTriple(llvm::raw_ostream &s) const { + const llvm::Triple &triple = GetTriple(); + llvm::StringRef arch_str = triple.getArchName(); + llvm::StringRef vendor_str = triple.getVendorName(); + llvm::StringRef os_str = triple.getOSName(); + llvm::StringRef environ_str = triple.getEnvironmentName(); + + s << llvm::formatv("{0}-{1}-{2}", arch_str.empty() ? "*" : arch_str, + vendor_str.empty() ? "*" : vendor_str, + os_str.empty() ? "*" : os_str); + + if (!environ_str.empty()) + s << "-" << environ_str; +} diff --git a/contrib/llvm-project/lldb/source/Utility/Args.cpp b/contrib/llvm-project/lldb/source/Utility/Args.cpp new file mode 100644 index 000000000000..13b993bc74c9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Args.cpp @@ -0,0 +1,690 @@ +//===-- Args.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/Utility/Args.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace lldb; +using namespace lldb_private; + +// A helper function for argument parsing. +// Parses the initial part of the first argument using normal double quote +// rules: backslash escapes the double quote and itself. The parsed string is +// appended to the second argument. The function returns the unparsed portion +// of the string, starting at the closing quote. +static llvm::StringRef ParseDoubleQuotes(llvm::StringRef quoted, + std::string &result) { + // Inside double quotes, '\' and '"' are special. + static const char *k_escapable_characters = "\"\\"; + while (true) { + // Skip over regular characters and append them. + size_t regular = quoted.find_first_of(k_escapable_characters); + result += quoted.substr(0, regular); + quoted = quoted.substr(regular); + + // If we have reached the end of string or the closing quote, we're done. + if (quoted.empty() || quoted.front() == '"') + break; + + // We have found a backslash. + quoted = quoted.drop_front(); + + if (quoted.empty()) { + // A lone backslash at the end of string, let's just append it. + result += '\\'; + break; + } + + // If the character after the backslash is not an allowed escapable + // character, we leave the character sequence untouched. + if (strchr(k_escapable_characters, quoted.front()) == nullptr) + result += '\\'; + + result += quoted.front(); + quoted = quoted.drop_front(); + } + + return quoted; +} + +static size_t ArgvToArgc(const char **argv) { + if (!argv) + return 0; + size_t count = 0; + while (*argv++) + ++count; + return count; +} + +// Trims all whitespace that can separate command line arguments from the left +// side of the string. +static llvm::StringRef ltrimForArgs(llvm::StringRef str) { + static const char *k_space_separators = " \t"; + return str.ltrim(k_space_separators); +} + +// A helper function for SetCommandString. Parses a single argument from the +// command string, processing quotes and backslashes in a shell-like manner. +// The function returns a tuple consisting of the parsed argument, the quote +// char used, and the unparsed portion of the string starting at the first +// unqouted, unescaped whitespace character. +static std::tuple<std::string, char, llvm::StringRef> +ParseSingleArgument(llvm::StringRef command) { + // Argument can be split into multiple discontiguous pieces, for example: + // "Hello ""World" + // this would result in a single argument "Hello World" (without the quotes) + // since the quotes would be removed and there is not space between the + // strings. + std::string arg; + + // Since we can have multiple quotes that form a single command in a command + // like: "Hello "world'!' (which will make a single argument "Hello world!") + // we remember the first quote character we encounter and use that for the + // quote character. + char first_quote_char = '\0'; + + bool arg_complete = false; + do { + // Skip over regular characters and append them. + size_t regular = command.find_first_of(" \t\r\"'`\\"); + arg += command.substr(0, regular); + command = command.substr(regular); + + if (command.empty()) + break; + + char special = command.front(); + command = command.drop_front(); + switch (special) { + case '\\': + if (command.empty()) { + arg += '\\'; + break; + } + + // If the character after the backslash is not an allowed escapable + // character, we leave the character sequence untouched. + if (strchr(" \t\\'\"`", command.front()) == nullptr) + arg += '\\'; + + arg += command.front(); + command = command.drop_front(); + + break; + + case ' ': + case '\t': + case '\r': + // We are not inside any quotes, we just found a space after an argument. + // We are done. + arg_complete = true; + break; + + case '"': + case '\'': + case '`': + // We found the start of a quote scope. + if (first_quote_char == '\0') + first_quote_char = special; + + if (special == '"') + command = ParseDoubleQuotes(command, arg); + else { + // For single quotes, we simply skip ahead to the matching quote + // character (or the end of the string). + size_t quoted = command.find(special); + arg += command.substr(0, quoted); + command = command.substr(quoted); + } + + // If we found a closing quote, skip it. + if (!command.empty()) + command = command.drop_front(); + + break; + } + } while (!arg_complete); + + return std::make_tuple(arg, first_quote_char, command); +} + +Args::ArgEntry::ArgEntry(llvm::StringRef str, char quote) : quote(quote) { + size_t size = str.size(); + ptr.reset(new char[size + 1]); + + ::memcpy(data(), str.data() ? str.data() : "", size); + ptr[size] = 0; +} + +// Args constructor +Args::Args(llvm::StringRef command) { SetCommandString(command); } + +Args::Args(const Args &rhs) { *this = rhs; } + +Args::Args(const StringList &list) : Args() { + for (const std::string &arg : list) + AppendArgument(arg); +} + +Args::Args(llvm::ArrayRef<llvm::StringRef> args) : Args() { + for (llvm::StringRef arg : args) + AppendArgument(arg); +} + +Args &Args::operator=(const Args &rhs) { + Clear(); + + m_argv.clear(); + m_entries.clear(); + for (auto &entry : rhs.m_entries) { + m_entries.emplace_back(entry.ref(), entry.quote); + m_argv.push_back(m_entries.back().data()); + } + m_argv.push_back(nullptr); + return *this; +} + +// Destructor +Args::~Args() = default; + +void Args::Dump(Stream &s, const char *label_name) const { + if (!label_name) + return; + + int i = 0; + for (auto &entry : m_entries) { + s.Indent(); + s.Format("{0}[{1}]=\"{2}\"\n", label_name, i++, entry.ref()); + } + s.Format("{0}[{1}]=NULL\n", label_name, i); + s.EOL(); +} + +bool Args::GetCommandString(std::string &command) const { + command.clear(); + + for (size_t i = 0; i < m_entries.size(); ++i) { + if (i > 0) + command += ' '; + char quote = m_entries[i].quote; + if (quote != '\0') + command += quote; + command += m_entries[i].ref(); + if (quote != '\0') + command += quote; + } + + return !m_entries.empty(); +} + +bool Args::GetQuotedCommandString(std::string &command) const { + command.clear(); + + for (size_t i = 0; i < m_entries.size(); ++i) { + if (i > 0) + command += ' '; + + if (m_entries[i].quote) { + command += m_entries[i].quote; + command += m_entries[i].ref(); + command += m_entries[i].quote; + } else { + command += m_entries[i].ref(); + } + } + + return !m_entries.empty(); +} + +void Args::SetCommandString(llvm::StringRef command) { + Clear(); + m_argv.clear(); + + command = ltrimForArgs(command); + std::string arg; + char quote; + while (!command.empty()) { + std::tie(arg, quote, command) = ParseSingleArgument(command); + m_entries.emplace_back(arg, quote); + m_argv.push_back(m_entries.back().data()); + command = ltrimForArgs(command); + } + m_argv.push_back(nullptr); +} + +const char *Args::GetArgumentAtIndex(size_t idx) const { + if (idx < m_argv.size()) + return m_argv[idx]; + return nullptr; +} + +char **Args::GetArgumentVector() { + assert(!m_argv.empty()); + // TODO: functions like execve and posix_spawnp exhibit undefined behavior + // when argv or envp is null. So the code below is actually wrong. However, + // other code in LLDB depends on it being null. The code has been acting + // this way for some time, so it makes sense to leave it this way until + // someone has the time to come along and fix it. + return (m_argv.size() > 1) ? m_argv.data() : nullptr; +} + +const char **Args::GetConstArgumentVector() const { + assert(!m_argv.empty()); + return (m_argv.size() > 1) ? const_cast<const char **>(m_argv.data()) + : nullptr; +} + +void Args::Shift() { + // Don't pop the last NULL terminator from the argv array + if (m_entries.empty()) + return; + m_argv.erase(m_argv.begin()); + m_entries.erase(m_entries.begin()); +} + +void Args::Unshift(llvm::StringRef arg_str, char quote_char) { + InsertArgumentAtIndex(0, arg_str, quote_char); +} + +void Args::AppendArguments(const Args &rhs) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + m_argv.pop_back(); + for (auto &entry : rhs.m_entries) { + m_entries.emplace_back(entry.ref(), entry.quote); + m_argv.push_back(m_entries.back().data()); + } + m_argv.push_back(nullptr); +} + +void Args::AppendArguments(const char **argv) { + size_t argc = ArgvToArgc(argv); + + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + m_argv.pop_back(); + for (auto arg : llvm::ArrayRef(argv, argc)) { + m_entries.emplace_back(arg, '\0'); + m_argv.push_back(m_entries.back().data()); + } + + m_argv.push_back(nullptr); +} + +void Args::AppendArgument(llvm::StringRef arg_str, char quote_char) { + InsertArgumentAtIndex(GetArgumentCount(), arg_str, quote_char); +} + +void Args::InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + + if (idx > m_entries.size()) + return; + m_entries.emplace(m_entries.begin() + idx, arg_str, quote_char); + m_argv.insert(m_argv.begin() + idx, m_entries[idx].data()); +} + +void Args::ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, + char quote_char) { + assert(m_argv.size() == m_entries.size() + 1); + assert(m_argv.back() == nullptr); + + if (idx >= m_entries.size()) + return; + + m_entries[idx] = ArgEntry(arg_str, quote_char); + m_argv[idx] = m_entries[idx].data(); +} + +void Args::DeleteArgumentAtIndex(size_t idx) { + if (idx >= m_entries.size()) + return; + + m_argv.erase(m_argv.begin() + idx); + m_entries.erase(m_entries.begin() + idx); +} + +void Args::SetArguments(size_t argc, const char **argv) { + Clear(); + + auto args = llvm::ArrayRef(argv, argc); + m_entries.resize(argc); + m_argv.resize(argc + 1); + for (size_t i = 0; i < args.size(); ++i) { + char quote = + ((args[i][0] == '\'') || (args[i][0] == '"') || (args[i][0] == '`')) + ? args[i][0] + : '\0'; + + m_entries[i] = ArgEntry(args[i], quote); + m_argv[i] = m_entries[i].data(); + } +} + +void Args::SetArguments(const char **argv) { + SetArguments(ArgvToArgc(argv), argv); +} + +void Args::Clear() { + m_entries.clear(); + m_argv.clear(); + m_argv.push_back(nullptr); +} + +std::string Args::GetShellSafeArgument(const FileSpec &shell, + llvm::StringRef unsafe_arg) { + struct ShellDescriptor { + llvm::StringRef m_basename; + llvm::StringRef m_escapables; + }; + + static ShellDescriptor g_Shells[] = {{"bash", " '\"<>()&;"}, + {"fish", " '\"<>()&\\|;"}, + {"tcsh", " '\"<>()&;"}, + {"zsh", " '\"<>()&;\\|"}, + {"sh", " '\"<>()&;"}}; + + // safe minimal set + llvm::StringRef escapables = " '\""; + + auto basename = shell.GetFilename().GetStringRef(); + if (!basename.empty()) { + for (const auto &Shell : g_Shells) { + if (Shell.m_basename == basename) { + escapables = Shell.m_escapables; + break; + } + } + } + + std::string safe_arg; + safe_arg.reserve(unsafe_arg.size()); + // Add a \ before every character that needs to be escaped. + for (char c : unsafe_arg) { + if (escapables.contains(c)) + safe_arg.push_back('\\'); + safe_arg.push_back(c); + } + return safe_arg; +} + +lldb::Encoding Args::StringToEncoding(llvm::StringRef s, + lldb::Encoding fail_value) { + return llvm::StringSwitch<lldb::Encoding>(s) + .Case("uint", eEncodingUint) + .Case("sint", eEncodingSint) + .Case("ieee754", eEncodingIEEE754) + .Case("vector", eEncodingVector) + .Default(fail_value); +} + +uint32_t Args::StringToGenericRegister(llvm::StringRef s) { + if (s.empty()) + return LLDB_INVALID_REGNUM; + uint32_t result = llvm::StringSwitch<uint32_t>(s) + .Case("pc", LLDB_REGNUM_GENERIC_PC) + .Case("sp", LLDB_REGNUM_GENERIC_SP) + .Case("fp", LLDB_REGNUM_GENERIC_FP) + .Cases("ra", "lr", LLDB_REGNUM_GENERIC_RA) + .Case("flags", LLDB_REGNUM_GENERIC_FLAGS) + .Case("arg1", LLDB_REGNUM_GENERIC_ARG1) + .Case("arg2", LLDB_REGNUM_GENERIC_ARG2) + .Case("arg3", LLDB_REGNUM_GENERIC_ARG3) + .Case("arg4", LLDB_REGNUM_GENERIC_ARG4) + .Case("arg5", LLDB_REGNUM_GENERIC_ARG5) + .Case("arg6", LLDB_REGNUM_GENERIC_ARG6) + .Case("arg7", LLDB_REGNUM_GENERIC_ARG7) + .Case("arg8", LLDB_REGNUM_GENERIC_ARG8) + .Case("tp", LLDB_REGNUM_GENERIC_TP) + .Default(LLDB_INVALID_REGNUM); + return result; +} + +void Args::EncodeEscapeSequences(const char *src, std::string &dst) { + dst.clear(); + if (src) { + for (const char *p = src; *p != '\0'; ++p) { + size_t non_special_chars = ::strcspn(p, "\\"); + if (non_special_chars > 0) { + dst.append(p, non_special_chars); + p += non_special_chars; + if (*p == '\0') + break; + } + + if (*p == '\\') { + ++p; // skip the slash + switch (*p) { + case 'a': + dst.append(1, '\a'); + break; + case 'b': + dst.append(1, '\b'); + break; + case 'f': + dst.append(1, '\f'); + break; + case 'n': + dst.append(1, '\n'); + break; + case 'r': + dst.append(1, '\r'); + break; + case 't': + dst.append(1, '\t'); + break; + case 'v': + dst.append(1, '\v'); + break; + case '\\': + dst.append(1, '\\'); + break; + case '\'': + dst.append(1, '\''); + break; + case '"': + dst.append(1, '"'); + break; + case '0': + // 1 to 3 octal chars + { + // Make a string that can hold onto the initial zero char, up to 3 + // octal digits, and a terminating NULL. + char oct_str[5] = {'\0', '\0', '\0', '\0', '\0'}; + + int i; + for (i = 0; (p[i] >= '0' && p[i] <= '7') && i < 4; ++i) + oct_str[i] = p[i]; + + // We don't want to consume the last octal character since the main + // for loop will do this for us, so we advance p by one less than i + // (even if i is zero) + p += i - 1; + unsigned long octal_value = ::strtoul(oct_str, nullptr, 8); + if (octal_value <= UINT8_MAX) { + dst.append(1, static_cast<char>(octal_value)); + } + } + break; + + case 'x': + // hex number in the format + if (isxdigit(p[1])) { + ++p; // Skip the 'x' + + // Make a string that can hold onto two hex chars plus a + // NULL terminator + char hex_str[3] = {*p, '\0', '\0'}; + if (isxdigit(p[1])) { + ++p; // Skip the first of the two hex chars + hex_str[1] = *p; + } + + unsigned long hex_value = strtoul(hex_str, nullptr, 16); + if (hex_value <= UINT8_MAX) + dst.append(1, static_cast<char>(hex_value)); + } else { + dst.append(1, 'x'); + } + break; + + default: + // Just desensitize any other character by just printing what came + // after the '\' + dst.append(1, *p); + break; + } + } + } + } +} + +void Args::ExpandEscapedCharacters(const char *src, std::string &dst) { + dst.clear(); + if (src) { + for (const char *p = src; *p != '\0'; ++p) { + if (llvm::isPrint(*p)) + dst.append(1, *p); + else { + switch (*p) { + case '\a': + dst.append("\\a"); + break; + case '\b': + dst.append("\\b"); + break; + case '\f': + dst.append("\\f"); + break; + case '\n': + dst.append("\\n"); + break; + case '\r': + dst.append("\\r"); + break; + case '\t': + dst.append("\\t"); + break; + case '\v': + dst.append("\\v"); + break; + case '\'': + dst.append("\\'"); + break; + case '"': + dst.append("\\\""); + break; + case '\\': + dst.append("\\\\"); + break; + default: { + // Just encode as octal + dst.append("\\0"); + char octal_str[32]; + snprintf(octal_str, sizeof(octal_str), "%o", *p); + dst.append(octal_str); + } break; + } + } + } + } +} + +std::string Args::EscapeLLDBCommandArgument(const std::string &arg, + char quote_char) { + const char *chars_to_escape = nullptr; + switch (quote_char) { + case '\0': + chars_to_escape = " \t\\'\"`"; + break; + case '"': + chars_to_escape = "$\"`\\"; + break; + case '`': + case '\'': + return arg; + default: + assert(false && "Unhandled quote character"); + return arg; + } + + std::string res; + res.reserve(arg.size()); + for (char c : arg) { + if (::strchr(chars_to_escape, c)) + res.push_back('\\'); + res.push_back(c); + } + return res; +} + +OptionsWithRaw::OptionsWithRaw(llvm::StringRef arg_string) { + SetFromString(arg_string); +} + +void OptionsWithRaw::SetFromString(llvm::StringRef arg_string) { + const llvm::StringRef original_args = arg_string; + + arg_string = ltrimForArgs(arg_string); + std::string arg; + char quote; + + // If the string doesn't start with a dash, we just have no options and just + // a raw part. + if (!arg_string.starts_with("-")) { + m_suffix = std::string(original_args); + return; + } + + bool found_suffix = false; + while (!arg_string.empty()) { + // The length of the prefix before parsing. + std::size_t prev_prefix_length = original_args.size() - arg_string.size(); + + // Parse the next argument from the remaining string. + std::tie(arg, quote, arg_string) = ParseSingleArgument(arg_string); + + // If we get an unquoted '--' argument, then we reached the suffix part + // of the command. + Args::ArgEntry entry(arg, quote); + if (!entry.IsQuoted() && arg == "--") { + // The remaining line is the raw suffix, and the line we parsed so far + // needs to be interpreted as arguments. + m_has_args = true; + m_suffix = std::string(arg_string); + found_suffix = true; + + // The length of the prefix after parsing. + std::size_t prefix_length = original_args.size() - arg_string.size(); + + // Take the string we know contains all the arguments and actually parse + // it as proper arguments. + llvm::StringRef prefix = original_args.take_front(prev_prefix_length); + m_args = Args(prefix); + m_arg_string = prefix; + + // We also record the part of the string that contains the arguments plus + // the delimiter. + m_arg_string_with_delimiter = original_args.take_front(prefix_length); + + // As the rest of the string became the raw suffix, we are done here. + break; + } + + arg_string = ltrimForArgs(arg_string); + } + + // If we didn't find a suffix delimiter, the whole string is the raw suffix. + if (!found_suffix) + m_suffix = std::string(original_args); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Baton.cpp b/contrib/llvm-project/lldb/source/Utility/Baton.cpp new file mode 100644 index 000000000000..d6bc8e308587 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Baton.cpp @@ -0,0 +1,13 @@ +//===-- Baton.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/Utility/Baton.h" + +void lldb_private::UntypedBaton::GetDescription(llvm::raw_ostream &s, + lldb::DescriptionLevel level, + unsigned indentation) const {} diff --git a/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp b/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp new file mode 100644 index 000000000000..c6b2606afe0c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp @@ -0,0 +1,550 @@ +//===-- Broadcaster.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/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" + +#include <algorithm> +#include <memory> +#include <utility> + +#include <cassert> +#include <cstddef> + +using namespace lldb; +using namespace lldb_private; + +Broadcaster::Broadcaster(BroadcasterManagerSP manager_sp, std::string name) + : m_broadcaster_sp(std::make_shared<BroadcasterImpl>(*this)), + m_manager_sp(std::move(manager_sp)), m_broadcaster_name(std::move(name)) { + Log *log = GetLog(LLDBLog::Object); + LLDB_LOG(log, "{0} Broadcaster::Broadcaster(\"{1}\")", + static_cast<void *>(this), GetBroadcasterName()); +} + +Broadcaster::BroadcasterImpl::BroadcasterImpl(Broadcaster &broadcaster) + : m_broadcaster(broadcaster), m_listeners(), m_listeners_mutex(), + m_hijacking_listeners(), m_hijacking_masks() {} + +Broadcaster::~Broadcaster() { + Log *log = GetLog(LLDBLog::Object); + LLDB_LOG(log, "{0} Broadcaster::~Broadcaster(\"{1}\")", + static_cast<void *>(this), GetBroadcasterName()); + + Clear(); +} + +void Broadcaster::CheckInWithManager() { + if (m_manager_sp) { + m_manager_sp->SignUpListenersForBroadcaster(*this); + } +} + +llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> +Broadcaster::BroadcasterImpl::GetListeners(uint32_t event_mask, + bool include_primary) { + llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> listeners; + size_t max_count = m_listeners.size(); + if (include_primary) + max_count++; + listeners.reserve(max_count); + + for (auto it = m_listeners.begin(); it != m_listeners.end();) { + lldb::ListenerSP curr_listener_sp(it->first.lock()); + if (curr_listener_sp) { + if (it->second & event_mask) + listeners.emplace_back(std::move(curr_listener_sp), it->second); + ++it; + } else + // If our listener_wp didn't resolve, then we should remove this entry. + it = m_listeners.erase(it); + } + if (include_primary && m_primary_listener_sp) + listeners.emplace_back(m_primary_listener_sp, m_primary_listener_mask); + + return listeners; +} + +bool Broadcaster::BroadcasterImpl::HasListeners(uint32_t event_mask) { + if (m_primary_listener_sp) + return true; + for (auto it = m_listeners.begin(); it != m_listeners.end(); it++) { + // Don't return a listener if the other end of the WP is gone: + lldb::ListenerSP curr_listener_sp(it->first.lock()); + if (curr_listener_sp && (it->second & event_mask)) + return true; + } + return false; +} + +void Broadcaster::BroadcasterImpl::Clear() { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + // Make sure the listener forgets about this broadcaster. We do this in the + // broadcaster in case the broadcaster object initiates the removal. + for (auto &pair : GetListeners()) + pair.first->BroadcasterWillDestruct(&m_broadcaster); + + m_listeners.clear(); + m_primary_listener_sp.reset(); +} + +Broadcaster *Broadcaster::BroadcasterImpl::GetBroadcaster() { + return &m_broadcaster; +} + +bool Broadcaster::BroadcasterImpl::GetEventNames( + Stream &s, uint32_t event_mask, bool prefix_with_broadcaster_name) const { + uint32_t num_names_added = 0; + if (event_mask && !m_event_names.empty()) { + event_names_map::const_iterator end = m_event_names.end(); + for (uint32_t bit = 1u, mask = event_mask; mask != 0 && bit != 0; + bit <<= 1, mask >>= 1) { + if (mask & 1) { + event_names_map::const_iterator pos = m_event_names.find(bit); + if (pos != end) { + if (num_names_added > 0) + s.PutCString(", "); + + if (prefix_with_broadcaster_name) { + s.PutCString(GetBroadcasterName()); + s.PutChar('.'); + } + s.PutCString(pos->second); + ++num_names_added; + } + } + } + } + return num_names_added > 0; +} + +void Broadcaster::AddInitialEventsToListener( + const lldb::ListenerSP &listener_sp, uint32_t requested_events) {} + +uint32_t +Broadcaster::BroadcasterImpl::AddListener(const lldb::ListenerSP &listener_sp, + uint32_t event_mask) { + if (!listener_sp) + return 0; + + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + // See if we already have this listener, and if so, update its mask + + bool handled = false; + + if (listener_sp == m_primary_listener_sp) + // This already handles all bits so just return the mask: + return event_mask; + + for (auto &pair : GetListeners(UINT32_MAX, false)) { + if (pair.first == listener_sp) { + handled = true; + pair.second |= event_mask; + m_broadcaster.AddInitialEventsToListener(listener_sp, event_mask); + break; + } + } + + if (!handled) { + // Grant a new listener the available event bits + m_listeners.push_back( + std::make_pair(lldb::ListenerWP(listener_sp), event_mask)); + + // Individual broadcasters decide whether they have outstanding data when a + // listener attaches, and insert it into the listener with this method. + m_broadcaster.AddInitialEventsToListener(listener_sp, event_mask); + } + + // Return the event bits that were granted to the listener + return event_mask; +} + +bool Broadcaster::BroadcasterImpl::EventTypeHasListeners(uint32_t event_type) { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back()) + return true; + + // The primary listener listens for all event bits: + if (m_primary_listener_sp) + return true; + + return HasListeners(event_type); +} + +bool Broadcaster::BroadcasterImpl::RemoveListener( + lldb_private::Listener *listener, uint32_t event_mask) { + if (!listener) + return false; + + if (listener == m_primary_listener_sp.get()) { + // Primary listeners listen for all the event bits for their broadcaster, + // so remove this altogether if asked: + m_primary_listener_sp.reset(); + return true; + } + + std::lock_guard<std::mutex> guard(m_listeners_mutex); + for (auto it = m_listeners.begin(); it != m_listeners.end();) { + lldb::ListenerSP curr_listener_sp(it->first.lock()); + + if (!curr_listener_sp) { + // The weak pointer for this listener didn't resolve, lets' prune it + // as we go. + it = m_listeners.erase(it); + continue; + } + + if (curr_listener_sp.get() == listener) { + it->second &= ~event_mask; + // If we removed all the event bits from a listener, remove it from + // the list as well. + if (!it->second) + m_listeners.erase(it); + return true; + } + it++; + } + return false; +} + +bool Broadcaster::BroadcasterImpl::RemoveListener( + const lldb::ListenerSP &listener_sp, uint32_t event_mask) { + return RemoveListener(listener_sp.get(), event_mask); +} + +void Broadcaster::BroadcasterImpl::BroadcastEvent(EventSP &event_sp) { + return PrivateBroadcastEvent(event_sp, false); +} + +void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(EventSP &event_sp) { + return PrivateBroadcastEvent(event_sp, true); +} + +void Broadcaster::BroadcasterImpl::PrivateBroadcastEvent(EventSP &event_sp, + bool unique) { + // Can't add a nullptr event... + if (!event_sp) + return; + + // Update the broadcaster on this event + event_sp->SetBroadcaster(&m_broadcaster); + + const uint32_t event_type = event_sp->GetType(); + + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + ListenerSP hijacking_listener_sp; + + if (!m_hijacking_listeners.empty()) { + assert(!m_hijacking_masks.empty()); + hijacking_listener_sp = m_hijacking_listeners.back(); + if ((event_type & m_hijacking_masks.back()) == 0) + hijacking_listener_sp.reset(); + } + + Log *log = GetLog(LLDBLog::Events); + if (!log && event_sp->GetData()) + log = event_sp->GetData()->GetLogChannel(); + + if (log) { + StreamString event_description; + event_sp->Dump(&event_description); + LLDB_LOG(log, + "{0:x} Broadcaster(\"{1}\")::BroadcastEvent (event_sp = {2}, " + "unique={3}) hijack = {4:x}", + static_cast<void *>(this), GetBroadcasterName(), + event_description.GetData(), unique, + static_cast<void *>(hijacking_listener_sp.get())); + } + ListenerSP primary_listener_sp + = hijacking_listener_sp ? hijacking_listener_sp : m_primary_listener_sp; + + if (primary_listener_sp) { + if (unique && primary_listener_sp->PeekAtNextEventForBroadcasterWithType( + &m_broadcaster, event_type)) + return; + // Add the pending listeners but not if the event is hijacked, since that + // is given sole access to the event stream it is hijacking. + // Make sure to do this before adding the event to the primary or it might + // start handling the event before we're done adding all the pending + // listeners. + // Also, don't redo the check for unique here, since otherwise that could + // be racy, and if we send the event to the primary listener then we SHOULD + // send it to the secondary listeners or they will get out of sync with the + // primary listener. + if (!hijacking_listener_sp) { + for (auto &pair : GetListeners(event_type, false)) + event_sp->AddPendingListener(pair.first); + } + primary_listener_sp->AddEvent(event_sp); + } else { + for (auto &pair : GetListeners(event_type)) { + if (unique && pair.first->PeekAtNextEventForBroadcasterWithType( + &m_broadcaster, event_type)) + continue; + + pair.first->AddEvent(event_sp); + } + } +} + +void Broadcaster::BroadcasterImpl::BroadcastEvent(uint32_t event_type) { + auto event_sp = std::make_shared<Event>(event_type, /*data = */ nullptr); + PrivateBroadcastEvent(event_sp, false); +} + +void Broadcaster::BroadcasterImpl::BroadcastEvent( + uint32_t event_type, const lldb::EventDataSP &event_data_sp) { + auto event_sp = std::make_shared<Event>(event_type, event_data_sp); + PrivateBroadcastEvent(event_sp, false); +} + +void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(uint32_t event_type) { + auto event_sp = std::make_shared<Event>(event_type, /*data = */ nullptr); + PrivateBroadcastEvent(event_sp, true); +} + +void Broadcaster::BroadcasterImpl::SetPrimaryListener(lldb::ListenerSP + listener_sp) { + // This might have already been added as a normal listener, make sure we + // don't hold two copies. + RemoveListener(listener_sp.get(), UINT32_MAX); + m_primary_listener_sp = listener_sp; + +} + +bool Broadcaster::BroadcasterImpl::HijackBroadcaster( + const lldb::ListenerSP &listener_sp, uint32_t event_mask) { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + Log *log = GetLog(LLDBLog::Events); + LLDB_LOG( + log, + "{0} Broadcaster(\"{1}\")::HijackBroadcaster (listener(\"{2}\")={3})", + static_cast<void *>(this), GetBroadcasterName(), + listener_sp->m_name.c_str(), static_cast<void *>(listener_sp.get())); + m_hijacking_listeners.push_back(listener_sp); + m_hijacking_masks.push_back(event_mask); + return true; +} + +bool Broadcaster::BroadcasterImpl::IsHijackedForEvent(uint32_t event_mask) { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + if (!m_hijacking_listeners.empty()) + return (event_mask & m_hijacking_masks.back()) != 0; + return false; +} + +const char *Broadcaster::BroadcasterImpl::GetHijackingListenerName() { + if (m_hijacking_listeners.size()) { + return m_hijacking_listeners.back()->GetName(); + } + return nullptr; +} + +void Broadcaster::BroadcasterImpl::RestoreBroadcaster() { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + if (!m_hijacking_listeners.empty()) { + ListenerSP listener_sp = m_hijacking_listeners.back(); + Log *log = GetLog(LLDBLog::Events); + LLDB_LOG(log, + "{0} Broadcaster(\"{1}\")::RestoreBroadcaster (about to pop " + "listener(\"{2}\")={3})", + static_cast<void *>(this), GetBroadcasterName(), + listener_sp->m_name.c_str(), + static_cast<void *>(listener_sp.get())); + m_hijacking_listeners.pop_back(); + } + if (!m_hijacking_masks.empty()) + m_hijacking_masks.pop_back(); +} + +llvm::StringRef Broadcaster::GetBroadcasterClass() const { + static constexpr llvm::StringLiteral class_name("lldb.anonymous"); + return class_name; +} + +bool BroadcastEventSpec::operator<(const BroadcastEventSpec &rhs) const { + if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) { + return GetEventBits() < rhs.GetEventBits(); + } + return GetBroadcasterClass() < rhs.GetBroadcasterClass(); +} + +BroadcasterManager::BroadcasterManager() : m_manager_mutex() {} + +lldb::BroadcasterManagerSP BroadcasterManager::MakeBroadcasterManager() { + return lldb::BroadcasterManagerSP(new BroadcasterManager()); +} + +uint32_t BroadcasterManager::RegisterListenerForEventsNoLock( + const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + uint32_t available_bits = event_spec.GetEventBits(); + + auto class_matches = [&event_spec](const event_listener_key &input) -> bool { + return input.first.GetBroadcasterClass() == + event_spec.GetBroadcasterClass(); + }; + + while (iter != end_iter && + (iter = find_if(iter, end_iter, class_matches)) != end_iter) { + available_bits &= ~((*iter).first.GetEventBits()); + iter++; + } + + if (available_bits != 0) { + m_event_map.insert(event_listener_key( + BroadcastEventSpec(event_spec.GetBroadcasterClass(), available_bits), + listener_sp)); + m_listeners.insert(listener_sp); + } + + return available_bits; +} + +bool BroadcasterManager::UnregisterListenerForEventsNoLock( + const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { + bool removed_some = false; + + if (m_listeners.erase(listener_sp) == 0) + return false; + + auto listener_matches_and_shared_bits = + [&listener_sp, &event_spec](const event_listener_key &input) -> bool { + return input.first.GetBroadcasterClass() == + event_spec.GetBroadcasterClass() && + (input.first.GetEventBits() & event_spec.GetEventBits()) != 0 && + input.second == listener_sp; + }; + std::vector<BroadcastEventSpec> to_be_readded; + uint32_t event_bits_to_remove = event_spec.GetEventBits(); + + // Go through the map and delete the exact matches, and build a list of + // matches that weren't exact to re-add: + for (auto iter = m_event_map.begin(), end = m_event_map.end();;) { + iter = find_if(iter, end, listener_matches_and_shared_bits); + if (iter == end) + break; + uint32_t iter_event_bits = (*iter).first.GetEventBits(); + removed_some = true; + + if (event_bits_to_remove != iter_event_bits) { + uint32_t new_event_bits = iter_event_bits & ~event_bits_to_remove; + to_be_readded.emplace_back(event_spec.GetBroadcasterClass(), + new_event_bits); + } + iter = m_event_map.erase(iter); + } + + // Okay now add back the bits that weren't completely removed: + for (const auto &event : to_be_readded) { + m_event_map.insert(event_listener_key(event, listener_sp)); + } + + return removed_some; +} + +ListenerSP BroadcasterManager::GetListenerForEventSpec( + const BroadcastEventSpec &event_spec) const { + std::lock_guard<std::mutex> guard(m_manager_mutex); + + auto event_spec_matches = + [&event_spec](const event_listener_key &input) -> bool { + return input.first.IsContainedIn(event_spec); + }; + + auto iter = llvm::find_if(m_event_map, event_spec_matches); + if (iter != m_event_map.end()) + return (*iter).second; + + return nullptr; +} + +void BroadcasterManager::RemoveListener(Listener *listener) { + std::lock_guard<std::mutex> guard(m_manager_mutex); + auto listeners_predicate = + [&listener](const lldb::ListenerSP &input) -> bool { + return input.get() == listener; + }; + + if (auto iter = llvm::find_if(m_listeners, listeners_predicate); + iter != m_listeners.end()) + m_listeners.erase(iter); + + auto events_predicate = [listener](const event_listener_key &input) -> bool { + return input.second.get() == listener; + }; + + // TODO: use 'std::map::erase_if' when moving to c++20. + for (auto iter = m_event_map.begin(), end = m_event_map.end();;) { + iter = find_if(iter, end, events_predicate); + if (iter == end) + break; + + iter = m_event_map.erase(iter); + } +} + +void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) { + std::lock_guard<std::mutex> guard(m_manager_mutex); + + auto listener_matches = + [&listener_sp](const event_listener_key &input) -> bool { + return input.second == listener_sp; + }; + + if (m_listeners.erase(listener_sp) == 0) + return; + + // TODO: use 'std::map::erase_if' when moving to c++20. + for (auto iter = m_event_map.begin(), end_iter = m_event_map.end();;) { + iter = find_if(iter, end_iter, listener_matches); + if (iter == end_iter) + break; + + iter = m_event_map.erase(iter); + } +} + +void BroadcasterManager::SignUpListenersForBroadcaster( + Broadcaster &broadcaster) { + std::lock_guard<std::mutex> guard(m_manager_mutex); + + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + + auto class_matches = [&broadcaster](const event_listener_key &input) -> bool { + return input.first.GetBroadcasterClass() == + broadcaster.GetBroadcasterClass(); + }; + + while (iter != end_iter && + (iter = find_if(iter, end_iter, class_matches)) != end_iter) { + (*iter).second->StartListeningForEvents(&broadcaster, + (*iter).first.GetEventBits()); + iter++; + } +} + +void BroadcasterManager::Clear() { + std::lock_guard<std::mutex> guard(m_manager_mutex); + + for (auto &listener : m_listeners) + listener->BroadcasterManagerWillDestruct(this->shared_from_this()); + m_listeners.clear(); + m_event_map.clear(); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Checksum.cpp b/contrib/llvm-project/lldb/source/Utility/Checksum.cpp new file mode 100644 index 000000000000..8943b4e12852 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Checksum.cpp @@ -0,0 +1,44 @@ +//===-- Checksum.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/Utility/Checksum.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" + +using namespace lldb_private; + +Checksum::Checksum(llvm::MD5::MD5Result md5) { SetMD5(md5); } + +Checksum::Checksum(const Checksum &checksum) { SetMD5(checksum.m_checksum); } + +Checksum &Checksum::operator=(const Checksum &checksum) { + SetMD5(checksum.m_checksum); + return *this; +} + +void Checksum::SetMD5(llvm::MD5::MD5Result md5) { + const constexpr size_t md5_length = 16; + std::uninitialized_copy_n(md5.begin(), md5_length, m_checksum.begin()); +} + +Checksum::operator bool() const { return !llvm::equal(m_checksum, g_sentinel); } + +bool Checksum::operator==(const Checksum &checksum) const { + return llvm::equal(m_checksum, checksum.m_checksum); +} + +bool Checksum::operator!=(const Checksum &checksum) const { + return !(*this == checksum); +} + +std::string Checksum::digest() const { + return std::string(m_checksum.digest()); +} + +llvm::MD5::MD5Result Checksum::g_sentinel = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; diff --git a/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp b/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp new file mode 100644 index 000000000000..e12609ca75e7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp @@ -0,0 +1,81 @@ +//===-- CompletionRequest.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/Utility/CompletionRequest.h" + +using namespace lldb; +using namespace lldb_private; + +CompletionRequest::CompletionRequest(llvm::StringRef command_line, + unsigned raw_cursor_pos, + CompletionResult &result) + : m_command(command_line), m_raw_cursor_pos(raw_cursor_pos), + m_result(result) { + assert(raw_cursor_pos <= command_line.size() && "Out of bounds cursor?"); + + // We parse the argument up to the cursor, so the last argument in + // parsed_line is the one containing the cursor, and the cursor is after the + // last character. + llvm::StringRef partial_command(command_line.substr(0, raw_cursor_pos)); + m_parsed_line = Args(partial_command); + + if (GetParsedLine().GetArgumentCount() == 0) { + m_cursor_index = 0; + m_cursor_char_position = 0; + } else { + m_cursor_index = GetParsedLine().GetArgumentCount() - 1U; + m_cursor_char_position = + strlen(GetParsedLine().GetArgumentAtIndex(m_cursor_index)); + } + + // The cursor is after a space but the space is not part of the argument. + // Let's add an empty fake argument to the end to make sure the completion + // code. Note: The space could be part of the last argument when it's quoted. + if (partial_command.ends_with(" ") && + !GetCursorArgumentPrefix().ends_with(" ")) + AppendEmptyArgument(); +} + +std::string CompletionResult::Completion::GetUniqueKey() const { + + // We build a unique key for this pair of completion:description. We + // prefix the key with the length of the completion string. This prevents + // that we could get any collisions from completions pairs such as these: + // "foo:", "bar" would be "foo:bar", but will now be: "4foo:bar" + // "foo", ":bar" would be "foo:bar", but will now be: "3foo:bar" + + std::string result; + result.append(std::to_string(m_completion.size())); + result.append(m_completion); + result.append(std::to_string(static_cast<int>(m_mode))); + result.append(":"); + result.append(m_descripton); + return result; +} + +void CompletionResult::AddResult(llvm::StringRef completion, + llvm::StringRef description, + CompletionMode mode) { + Completion r(completion, description, mode); + + // Add the completion if we haven't seen the same value before. + if (m_added_values.insert(r.GetUniqueKey()).second) + m_results.push_back(r); +} + +void CompletionResult::GetMatches(StringList &matches) const { + matches.Clear(); + for (const Completion &completion : m_results) + matches.AppendString(completion.GetCompletion()); +} + +void CompletionResult::GetDescriptions(StringList &descriptions) const { + descriptions.Clear(); + for (const Completion &completion : m_results) + descriptions.AppendString(completion.GetDescription()); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Connection.cpp b/contrib/llvm-project/lldb/source/Utility/Connection.cpp new file mode 100644 index 000000000000..d5fcd4f3462f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Connection.cpp @@ -0,0 +1,13 @@ +//===-- Connection.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/Utility/Connection.h" + +using namespace lldb_private; + +Connection::~Connection() = default; diff --git a/contrib/llvm-project/lldb/source/Utility/ConstString.cpp b/contrib/llvm-project/lldb/source/Utility/ConstString.cpp new file mode 100644 index 000000000000..ea897dc611cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ConstString.cpp @@ -0,0 +1,343 @@ +//===-- ConstString.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/Utility/ConstString.h" + +#include "lldb/Utility/Stream.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/RWMutex.h" +#include "llvm/Support/Threading.h" + +#include <array> +#include <utility> + +#include <cinttypes> +#include <cstdint> +#include <cstring> + +using namespace lldb_private; + +class Pool { +public: + /// The default BumpPtrAllocatorImpl slab size. + static const size_t AllocatorSlabSize = 4096; + static const size_t SizeThreshold = AllocatorSlabSize; + /// Every Pool has its own allocator which receives an equal share of + /// the ConstString allocations. This means that when allocating many + /// ConstStrings, every allocator sees only its small share of allocations and + /// assumes LLDB only allocated a small amount of memory so far. In reality + /// LLDB allocated a total memory that is N times as large as what the + /// allocator sees (where N is the number of string pools). This causes that + /// the BumpPtrAllocator continues a long time to allocate memory in small + /// chunks which only makes sense when allocating a small amount of memory + /// (which is true from the perspective of a single allocator). On some + /// systems doing all these small memory allocations causes LLDB to spend + /// a lot of time in malloc, so we need to force all these allocators to + /// behave like one allocator in terms of scaling their memory allocations + /// with increased demand. To do this we set the growth delay for each single + /// allocator to a rate so that our pool of allocators scales their memory + /// allocations similar to a single BumpPtrAllocatorImpl. + /// + /// Currently we have 256 string pools and the normal growth delay of the + /// BumpPtrAllocatorImpl is 128 (i.e., the memory allocation size increases + /// every 128 full chunks), so by changing the delay to 1 we get a + /// total growth delay in our allocator collection of 256/1 = 256. This is + /// still only half as fast as a normal allocator but we can't go any faster + /// without decreasing the number of string pools. + static const size_t AllocatorGrowthDelay = 1; + typedef llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize, + SizeThreshold, AllocatorGrowthDelay> + Allocator; + typedef const char *StringPoolValueType; + typedef llvm::StringMap<StringPoolValueType, Allocator> StringPool; + typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType; + + static StringPoolEntryType & + GetStringMapEntryFromKeyData(const char *keyData) { + return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData); + } + + static size_t GetConstCStringLength(const char *ccstr) { + if (ccstr != nullptr) { + // Since the entry is read only, and we derive the entry entirely from + // the pointer, we don't need the lock. + const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr); + return entry.getKey().size(); + } + return 0; + } + + StringPoolValueType GetMangledCounterpart(const char *ccstr) { + if (ccstr != nullptr) { + const PoolEntry &pool = selectPool(llvm::StringRef(ccstr)); + llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex); + return GetStringMapEntryFromKeyData(ccstr).getValue(); + } + return nullptr; + } + + const char *GetConstCString(const char *cstr) { + if (cstr != nullptr) + return GetConstCStringWithLength(cstr, strlen(cstr)); + return nullptr; + } + + const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) { + if (cstr != nullptr) + return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len)); + return nullptr; + } + + const char *GetConstCStringWithStringRef(llvm::StringRef string_ref) { + if (string_ref.data()) { + const uint32_t string_hash = StringPool::hash(string_ref); + PoolEntry &pool = selectPool(string_hash); + + { + llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex); + auto it = pool.m_string_map.find(string_ref, string_hash); + if (it != pool.m_string_map.end()) + return it->getKeyData(); + } + + llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex); + StringPoolEntryType &entry = + *pool.m_string_map + .insert(std::make_pair(string_ref, nullptr), string_hash) + .first; + return entry.getKeyData(); + } + return nullptr; + } + + const char * + GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled, + const char *mangled_ccstr) { + const char *demangled_ccstr = nullptr; + + { + const uint32_t demangled_hash = StringPool::hash(demangled); + PoolEntry &pool = selectPool(demangled_hash); + llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex); + + // Make or update string pool entry with the mangled counterpart + StringPool &map = pool.m_string_map; + StringPoolEntryType &entry = + *map.try_emplace_with_hash(demangled, demangled_hash).first; + + entry.second = mangled_ccstr; + + // Extract the const version of the demangled_cstr + demangled_ccstr = entry.getKeyData(); + } + + { + // Now assign the demangled const string as the counterpart of the + // mangled const string... + PoolEntry &pool = selectPool(llvm::StringRef(mangled_ccstr)); + llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex); + GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr); + } + + // Return the constant demangled C string + return demangled_ccstr; + } + + const char *GetConstTrimmedCStringWithLength(const char *cstr, + size_t cstr_len) { + if (cstr != nullptr) { + const size_t trimmed_len = strnlen(cstr, cstr_len); + return GetConstCStringWithLength(cstr, trimmed_len); + } + return nullptr; + } + + ConstString::MemoryStats GetMemoryStats() const { + ConstString::MemoryStats stats; + for (const auto &pool : m_string_pools) { + llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex); + const Allocator &alloc = pool.m_string_map.getAllocator(); + stats.bytes_total += alloc.getTotalMemory(); + stats.bytes_used += alloc.getBytesAllocated(); + } + return stats; + } + +protected: + struct PoolEntry { + mutable llvm::sys::SmartRWMutex<false> m_mutex; + StringPool m_string_map; + }; + + std::array<PoolEntry, 256> m_string_pools; + + PoolEntry &selectPool(const llvm::StringRef &s) { + return selectPool(StringPool::hash(s)); + } + + PoolEntry &selectPool(uint32_t h) { + return m_string_pools[((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff]; + } +}; + +// Frameworks and dylibs aren't supposed to have global C++ initializers so we +// hide the string pool in a static function so that it will get initialized on +// the first call to this static function. +// +// Note, for now we make the string pool a pointer to the pool, because we +// can't guarantee that some objects won't get destroyed after the global +// destructor chain is run, and trying to make sure no destructors touch +// ConstStrings is difficult. So we leak the pool instead. +static Pool &StringPool() { + static llvm::once_flag g_pool_initialization_flag; + static Pool *g_string_pool = nullptr; + + llvm::call_once(g_pool_initialization_flag, + []() { g_string_pool = new Pool(); }); + + return *g_string_pool; +} + +ConstString::ConstString(const char *cstr) + : m_string(StringPool().GetConstCString(cstr)) {} + +ConstString::ConstString(const char *cstr, size_t cstr_len) + : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {} + +ConstString::ConstString(llvm::StringRef s) + : m_string(StringPool().GetConstCStringWithStringRef(s)) {} + +bool ConstString::operator<(ConstString rhs) const { + if (m_string == rhs.m_string) + return false; + + llvm::StringRef lhs_string_ref(GetStringRef()); + llvm::StringRef rhs_string_ref(rhs.GetStringRef()); + + // If both have valid C strings, then return the comparison + if (lhs_string_ref.data() && rhs_string_ref.data()) + return lhs_string_ref < rhs_string_ref; + + // Else one of them was nullptr, so if LHS is nullptr then it is less than + return lhs_string_ref.data() == nullptr; +} + +Stream &lldb_private::operator<<(Stream &s, ConstString str) { + const char *cstr = str.GetCString(); + if (cstr != nullptr) + s << cstr; + + return s; +} + +size_t ConstString::GetLength() const { + return Pool::GetConstCStringLength(m_string); +} + +bool ConstString::Equals(ConstString lhs, ConstString rhs, + const bool case_sensitive) { + if (lhs.m_string == rhs.m_string) + return true; + + // Since the pointers weren't equal, and identical ConstStrings always have + // identical pointers, the result must be false for case sensitive equality + // test. + if (case_sensitive) + return false; + + // perform case insensitive equality test + llvm::StringRef lhs_string_ref(lhs.GetStringRef()); + llvm::StringRef rhs_string_ref(rhs.GetStringRef()); + return lhs_string_ref.equals_insensitive(rhs_string_ref); +} + +int ConstString::Compare(ConstString lhs, ConstString rhs, + const bool case_sensitive) { + // If the iterators are the same, this is the same string + const char *lhs_cstr = lhs.m_string; + const char *rhs_cstr = rhs.m_string; + if (lhs_cstr == rhs_cstr) + return 0; + if (lhs_cstr && rhs_cstr) { + llvm::StringRef lhs_string_ref(lhs.GetStringRef()); + llvm::StringRef rhs_string_ref(rhs.GetStringRef()); + + if (case_sensitive) { + return lhs_string_ref.compare(rhs_string_ref); + } else { + return lhs_string_ref.compare_insensitive(rhs_string_ref); + } + } + + if (lhs_cstr) + return +1; // LHS isn't nullptr but RHS is + else + return -1; // LHS is nullptr but RHS isn't +} + +void ConstString::Dump(Stream *s, const char *fail_value) const { + if (s != nullptr) { + const char *cstr = AsCString(fail_value); + if (cstr != nullptr) + s->PutCString(cstr); + } +} + +void ConstString::DumpDebug(Stream *s) const { + const char *cstr = GetCString(); + size_t cstr_len = GetLength(); + // Only print the parens if we have a non-nullptr string + const char *parens = cstr ? "\"" : ""; + s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64, + static_cast<int>(sizeof(void *) * 2), + static_cast<const void *>(this), parens, cstr, parens, + static_cast<uint64_t>(cstr_len)); +} + +void ConstString::SetCString(const char *cstr) { + m_string = StringPool().GetConstCString(cstr); +} + +void ConstString::SetString(llvm::StringRef s) { + m_string = StringPool().GetConstCStringWithStringRef(s); +} + +void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled, + ConstString mangled) { + m_string = StringPool().GetConstCStringAndSetMangledCounterPart( + demangled, mangled.m_string); +} + +bool ConstString::GetMangledCounterpart(ConstString &counterpart) const { + counterpart.m_string = StringPool().GetMangledCounterpart(m_string); + return (bool)counterpart; +} + +void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) { + m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len); +} + +void ConstString::SetTrimmedCStringWithLength(const char *cstr, + size_t cstr_len) { + m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len); +} + +ConstString::MemoryStats ConstString::GetMemoryStats() { + return StringPool().GetMemoryStats(); +} + +void llvm::format_provider<ConstString>::format(const ConstString &CS, + llvm::raw_ostream &OS, + llvm::StringRef Options) { + format_provider<StringRef>::format(CS.GetStringRef(), OS, Options); +} diff --git a/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp b/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp new file mode 100644 index 000000000000..ea84a36f1697 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp @@ -0,0 +1,74 @@ +//===-- DataBufferHeap.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/Utility/DataBufferHeap.h" + + +using namespace lldb_private; + +// Default constructor +DataBufferHeap::DataBufferHeap() : m_data() {} + +// Initialize this class with "n" characters and fill the buffer with "ch". +DataBufferHeap::DataBufferHeap(lldb::offset_t n, uint8_t ch) : m_data() { + if (n < m_data.max_size()) + m_data.assign(n, ch); +} + +// Initialize this class with a copy of the "n" bytes from the "bytes" buffer. +DataBufferHeap::DataBufferHeap(const void *src, lldb::offset_t src_len) + : m_data() { + CopyData(src, src_len); +} + +DataBufferHeap::DataBufferHeap(const DataBuffer &data_buffer) : m_data() { + CopyData(data_buffer.GetBytes(), data_buffer.GetByteSize()); +} + +// Virtual destructor since this class inherits from a pure virtual base class. +DataBufferHeap::~DataBufferHeap() = default; + +// Return a const pointer to the bytes owned by this object, or nullptr if the +// object contains no bytes. +const uint8_t *DataBufferHeap::GetBytesImpl() const { + return (m_data.empty() ? nullptr : m_data.data()); +} + +// Return the number of bytes this object currently contains. +uint64_t DataBufferHeap::GetByteSize() const { return m_data.size(); } + +// Sets the number of bytes that this object should be able to contain. This +// can be used prior to copying data into the buffer. +uint64_t DataBufferHeap::SetByteSize(uint64_t new_size) { + if (new_size < m_data.max_size()) + m_data.resize(new_size); + return m_data.size(); +} + +void DataBufferHeap::CopyData(const void *src, uint64_t src_len) { + const uint8_t *src_u8 = static_cast<const uint8_t *>(src); + if (src && src_len > 0) + m_data.assign(src_u8, src_u8 + src_len); + else + m_data.clear(); +} + +void DataBufferHeap::AppendData(const void *src, uint64_t src_len) { + m_data.insert(m_data.end(), static_cast<const uint8_t *>(src), + static_cast<const uint8_t *>(src) + src_len); +} + +void DataBufferHeap::Clear() { + buffer_t empty; + m_data.swap(empty); +} + +char DataBuffer::ID; +char WritableDataBuffer::ID; +char DataBufferUnowned::ID; +char DataBufferHeap::ID; diff --git a/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp b/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp new file mode 100644 index 000000000000..376fe0689ecf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp @@ -0,0 +1,51 @@ +//===-- DataBufferLLVM.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/Utility/DataBufferLLVM.h" + +#include "llvm/Support/MemoryBuffer.h" + +#include <cassert> + +using namespace lldb_private; + +DataBufferLLVM::DataBufferLLVM(std::unique_ptr<llvm::MemoryBuffer> MemBuffer) + : Buffer(std::move(MemBuffer)) { + assert(Buffer != nullptr && + "Cannot construct a DataBufferLLVM with a null buffer"); +} + +DataBufferLLVM::~DataBufferLLVM() = default; + +const uint8_t *DataBufferLLVM::GetBytesImpl() const { + return reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()); +} + +lldb::offset_t DataBufferLLVM::GetByteSize() const { + return Buffer->getBufferSize(); +} + +WritableDataBufferLLVM::WritableDataBufferLLVM( + std::unique_ptr<llvm::WritableMemoryBuffer> MemBuffer) + : Buffer(std::move(MemBuffer)) { + assert(Buffer != nullptr && + "Cannot construct a WritableDataBufferLLVM with a null buffer"); +} + +WritableDataBufferLLVM::~WritableDataBufferLLVM() = default; + +const uint8_t *WritableDataBufferLLVM::GetBytesImpl() const { + return reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()); +} + +lldb::offset_t WritableDataBufferLLVM::GetByteSize() const { + return Buffer->getBufferSize(); +} + +char DataBufferLLVM::ID; +char WritableDataBufferLLVM::ID; diff --git a/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp b/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp new file mode 100644 index 000000000000..b21453977496 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp @@ -0,0 +1,191 @@ +//===-- DataEncoder.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/Utility/DataEncoder.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +#include <cstddef> + +#include <cstring> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::support::endian; + +DataEncoder::DataEncoder() + : m_data_sp(new DataBufferHeap()), m_byte_order(endian::InlHostByteOrder()), + m_addr_size(sizeof(void *)) {} + +DataEncoder::DataEncoder(const void *data, uint32_t length, ByteOrder endian, + uint8_t addr_size) + : m_data_sp(new DataBufferHeap(data, length)), m_byte_order(endian), + m_addr_size(addr_size) {} + +DataEncoder::DataEncoder(ByteOrder endian, uint8_t addr_size) + : m_data_sp(new DataBufferHeap()), m_byte_order(endian), + m_addr_size(addr_size) {} + +DataEncoder::~DataEncoder() = default; + +llvm::ArrayRef<uint8_t> DataEncoder::GetData() const { + return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(), GetByteSize()); +} + +size_t DataEncoder::GetByteSize() const { return m_data_sp->GetByteSize(); } + +// Extract a single unsigned char from the binary data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +uint32_t DataEncoder::PutU8(uint32_t offset, uint8_t value) { + if (ValidOffset(offset)) { + m_data_sp->GetBytes()[offset] = value; + return offset + 1; + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutU16(uint32_t offset, uint16_t value) { + if (ValidOffsetForDataOfSize(offset, sizeof(value))) { + if (m_byte_order != endian::InlHostByteOrder()) + write16be(m_data_sp->GetBytes() + offset, value); + else + write16le(m_data_sp->GetBytes() + offset, value); + + return offset + sizeof(value); + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutU32(uint32_t offset, uint32_t value) { + if (ValidOffsetForDataOfSize(offset, sizeof(value))) { + if (m_byte_order != endian::InlHostByteOrder()) + write32be(m_data_sp->GetBytes() + offset, value); + else + write32le(m_data_sp->GetBytes() + offset, value); + + return offset + sizeof(value); + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutU64(uint32_t offset, uint64_t value) { + if (ValidOffsetForDataOfSize(offset, sizeof(value))) { + if (m_byte_order != endian::InlHostByteOrder()) + write64be(m_data_sp->GetBytes() + offset, value); + else + write64le(m_data_sp->GetBytes() + offset, value); + + return offset + sizeof(value); + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutUnsigned(uint32_t offset, uint32_t byte_size, + uint64_t value) { + switch (byte_size) { + case 1: + return PutU8(offset, value); + case 2: + return PutU16(offset, value); + case 4: + return PutU32(offset, value); + case 8: + return PutU64(offset, value); + default: + llvm_unreachable("GetMax64 unhandled case!"); + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutData(uint32_t offset, const void *src, + uint32_t src_len) { + if (src == nullptr || src_len == 0) + return offset; + + if (ValidOffsetForDataOfSize(offset, src_len)) { + memcpy(m_data_sp->GetBytes() + offset, src, src_len); + return offset + src_len; + } + return UINT32_MAX; +} + +uint32_t DataEncoder::PutAddress(uint32_t offset, lldb::addr_t addr) { + return PutUnsigned(offset, m_addr_size, addr); +} + +uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) { + if (cstr != nullptr) + return PutData(offset, cstr, strlen(cstr) + 1); + return UINT32_MAX; +} + +void DataEncoder::AppendU8(uint8_t value) { + m_data_sp->AppendData(&value, sizeof(value)); +} + +void DataEncoder::AppendU16(uint16_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU16(offset, value); +} + +void DataEncoder::AppendU32(uint32_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU32(offset, value); +} + +void DataEncoder::AppendU64(uint64_t value) { + uint32_t offset = m_data_sp->GetByteSize(); + m_data_sp->SetByteSize(m_data_sp->GetByteSize() + sizeof(value)); + PutU64(offset, value); +} + +void DataEncoder::AppendAddress(lldb::addr_t addr) { + switch (m_addr_size) { + case 4: + AppendU32(addr); + break; + case 8: + AppendU64(addr); + break; + default: + llvm_unreachable("AppendAddress unhandled case!"); + } +} + +void DataEncoder::AppendData(llvm::StringRef data) { + const char *bytes = data.data(); + const size_t length = data.size(); + if (bytes && length > 0) + m_data_sp->AppendData(bytes, length); +} + +void DataEncoder::AppendData(llvm::ArrayRef<uint8_t> data) { + const uint8_t *bytes = data.data(); + const size_t length = data.size(); + if (bytes && length > 0) + m_data_sp->AppendData(bytes, length); +} + +void DataEncoder::AppendCString(llvm::StringRef data) { + const char *bytes = data.data(); + const size_t length = data.size(); + if (bytes) { + if (length > 0) + m_data_sp->AppendData(bytes, length); + if (length == 0 || bytes[length - 1] != '\0') + AppendU8(0); + } +} diff --git a/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp b/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp new file mode 100644 index 000000000000..e9be0cba81f0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp @@ -0,0 +1,1047 @@ +//===-- DataExtractor.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/Utility/DataExtractor.h" + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UUID.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/MathExtras.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstdint> +#include <string> + +#include <cctype> +#include <cinttypes> +#include <cstring> + +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t ReadInt16(const unsigned char *ptr, offset_t offset) { + uint16_t value; + memcpy(&value, ptr + offset, 2); + return value; +} + +static inline uint32_t ReadInt32(const unsigned char *ptr, + offset_t offset = 0) { + uint32_t value; + memcpy(&value, ptr + offset, 4); + return value; +} + +static inline uint64_t ReadInt64(const unsigned char *ptr, + offset_t offset = 0) { + uint64_t value; + memcpy(&value, ptr + offset, 8); + return value; +} + +static inline uint16_t ReadInt16(const void *ptr) { + uint16_t value; + memcpy(&value, ptr, 2); + return value; +} + +static inline uint16_t ReadSwapInt16(const unsigned char *ptr, + offset_t offset) { + uint16_t value; + memcpy(&value, ptr + offset, 2); + return llvm::byteswap<uint16_t>(value); +} + +static inline uint32_t ReadSwapInt32(const unsigned char *ptr, + offset_t offset) { + uint32_t value; + memcpy(&value, ptr + offset, 4); + return llvm::byteswap<uint32_t>(value); +} + +static inline uint64_t ReadSwapInt64(const unsigned char *ptr, + offset_t offset) { + uint64_t value; + memcpy(&value, ptr + offset, 8); + return llvm::byteswap<uint64_t>(value); +} + +static inline uint16_t ReadSwapInt16(const void *ptr) { + uint16_t value; + memcpy(&value, ptr, 2); + return llvm::byteswap<uint16_t>(value); +} + +static inline uint32_t ReadSwapInt32(const void *ptr) { + uint32_t value; + memcpy(&value, ptr, 4); + return llvm::byteswap<uint32_t>(value); +} + +static inline uint64_t ReadSwapInt64(const void *ptr) { + uint64_t value; + memcpy(&value, ptr, 8); + return llvm::byteswap<uint64_t>(value); +} + +static inline uint64_t ReadMaxInt64(const uint8_t *data, size_t byte_size, + ByteOrder byte_order) { + uint64_t res = 0; + if (byte_order == eByteOrderBig) + for (size_t i = 0; i < byte_size; ++i) + res = (res << 8) | data[i]; + else { + assert(byte_order == eByteOrderLittle); + for (size_t i = 0; i < byte_size; ++i) + res = (res << 8) | data[byte_size - 1 - i]; + } + return res; +} + +DataExtractor::DataExtractor() + : m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)), + m_data_sp() {} + +// This constructor allows us to use data that is owned by someone else. The +// data must stay around as long as this object is valid. +DataExtractor::DataExtractor(const void *data, offset_t length, + ByteOrder endian, uint32_t addr_size, + uint32_t target_byte_size /*=1*/) + : m_start(const_cast<uint8_t *>(static_cast<const uint8_t *>(data))), + m_end(const_cast<uint8_t *>(static_cast<const uint8_t *>(data)) + length), + m_byte_order(endian), m_addr_size(addr_size), m_data_sp(), + m_target_byte_size(target_byte_size) { + assert(addr_size >= 1 && addr_size <= 8); +} + +// Make a shared pointer reference to the shared data in "data_sp" and set the +// endian swapping setting to "swap", and the address size to "addr_size". The +// shared data reference will ensure the data lives as long as any +// DataExtractor objects exist that have a reference to this data. +DataExtractor::DataExtractor(const DataBufferSP &data_sp, ByteOrder endian, + uint32_t addr_size, + uint32_t target_byte_size /*=1*/) + : m_byte_order(endian), m_addr_size(addr_size), m_data_sp(), + m_target_byte_size(target_byte_size) { + assert(addr_size >= 1 && addr_size <= 8); + SetData(data_sp); +} + +// Initialize this object with a subset of the data bytes in "data". If "data" +// contains shared data, then a reference to this shared data will added and +// the shared data will stay around as long as any object contains a reference +// to that data. The endian swap and address size settings are copied from +// "data". +DataExtractor::DataExtractor(const DataExtractor &data, offset_t offset, + offset_t length, uint32_t target_byte_size /*=1*/) + : m_byte_order(data.m_byte_order), m_addr_size(data.m_addr_size), + m_data_sp(), m_target_byte_size(target_byte_size) { + assert(m_addr_size >= 1 && m_addr_size <= 8); + if (data.ValidOffset(offset)) { + offset_t bytes_available = data.GetByteSize() - offset; + if (length > bytes_available) + length = bytes_available; + SetData(data, offset, length); + } +} + +DataExtractor::DataExtractor(const DataExtractor &rhs) + : m_start(rhs.m_start), m_end(rhs.m_end), m_byte_order(rhs.m_byte_order), + m_addr_size(rhs.m_addr_size), m_data_sp(rhs.m_data_sp), + m_target_byte_size(rhs.m_target_byte_size) { + assert(m_addr_size >= 1 && m_addr_size <= 8); +} + +// Assignment operator +const DataExtractor &DataExtractor::operator=(const DataExtractor &rhs) { + if (this != &rhs) { + m_start = rhs.m_start; + m_end = rhs.m_end; + m_byte_order = rhs.m_byte_order; + m_addr_size = rhs.m_addr_size; + m_data_sp = rhs.m_data_sp; + } + return *this; +} + +DataExtractor::~DataExtractor() = default; + +// Clears the object contents back to a default invalid state, and release any +// references to shared data that this object may contain. +void DataExtractor::Clear() { + m_start = nullptr; + m_end = nullptr; + m_byte_order = endian::InlHostByteOrder(); + m_addr_size = sizeof(void *); + m_data_sp.reset(); +} + +// If this object contains shared data, this function returns the offset into +// that shared data. Else zero is returned. +size_t DataExtractor::GetSharedDataOffset() const { + if (m_start != nullptr) { + const DataBuffer *data = m_data_sp.get(); + if (data != nullptr) { + const uint8_t *data_bytes = data->GetBytes(); + if (data_bytes != nullptr) { + assert(m_start >= data_bytes); + return m_start - data_bytes; + } + } + } + return 0; +} + +// Set the data with which this object will extract from to data starting at +// BYTES and set the length of the data to LENGTH bytes long. The data is +// externally owned must be around at least as long as this object points to +// the data. No copy of the data is made, this object just refers to this data +// and can extract from it. If this object refers to any shared data upon +// entry, the reference to that data will be released. Is SWAP is set to true, +// any data extracted will be endian swapped. +lldb::offset_t DataExtractor::SetData(const void *bytes, offset_t length, + ByteOrder endian) { + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == nullptr || length == 0) { + m_start = nullptr; + m_end = nullptr; + } else { + m_start = const_cast<uint8_t *>(static_cast<const uint8_t *>(bytes)); + m_end = m_start + length; + } + return GetByteSize(); +} + +// Assign the data for this object to be a subrange in "data" starting +// "data_offset" bytes into "data" and ending "data_length" bytes later. If +// "data_offset" is not a valid offset into "data", then this object will +// contain no bytes. If "data_offset" is within "data" yet "data_length" is too +// large, the length will be capped at the number of bytes remaining in "data". +// If "data" contains a shared pointer to other data, then a ref counted +// pointer to that data will be made in this object. If "data" doesn't contain +// a shared pointer to data, then the bytes referred to in "data" will need to +// exist at least as long as this object refers to those bytes. The address +// size and endian swap settings are copied from the current values in "data". +lldb::offset_t DataExtractor::SetData(const DataExtractor &data, + offset_t data_offset, + offset_t data_length) { + m_addr_size = data.m_addr_size; + assert(m_addr_size >= 1 && m_addr_size <= 8); + // If "data" contains shared pointer to data, then we can use that + if (data.m_data_sp) { + m_byte_order = data.m_byte_order; + return SetData(data.m_data_sp, data.GetSharedDataOffset() + data_offset, + data_length); + } + + // We have a DataExtractor object that just has a pointer to bytes + if (data.ValidOffset(data_offset)) { + if (data_length > data.GetByteSize() - data_offset) + data_length = data.GetByteSize() - data_offset; + return SetData(data.GetDataStart() + data_offset, data_length, + data.GetByteOrder()); + } + return 0; +} + +// Assign the data for this object to be a subrange of the shared data in +// "data_sp" starting "data_offset" bytes into "data_sp" and ending +// "data_length" bytes later. If "data_offset" is not a valid offset into +// "data_sp", then this object will contain no bytes. If "data_offset" is +// within "data_sp" yet "data_length" is too large, the length will be capped +// at the number of bytes remaining in "data_sp". A ref counted pointer to the +// data in "data_sp" will be made in this object IF the number of bytes this +// object refers to in greater than zero (if at least one byte was available +// starting at "data_offset") to ensure the data stays around as long as it is +// needed. The address size and endian swap settings will remain unchanged from +// their current settings. +lldb::offset_t DataExtractor::SetData(const DataBufferSP &data_sp, + offset_t data_offset, + offset_t data_length) { + m_start = m_end = nullptr; + + if (data_length > 0) { + m_data_sp = data_sp; + if (data_sp) { + const size_t data_size = data_sp->GetByteSize(); + if (data_offset < data_size) { + m_start = data_sp->GetBytes() + data_offset; + const size_t bytes_left = data_size - data_offset; + // Cap the length of we asked for too many + if (data_length <= bytes_left) + m_end = m_start + data_length; // We got all the bytes we wanted + else + m_end = m_start + bytes_left; // Not all the bytes requested were + // available in the shared data + } + } + } + + size_t new_size = GetByteSize(); + + // Don't hold a shared pointer to the data buffer if we don't share any valid + // bytes in the shared buffer. + if (new_size == 0) + m_data_sp.reset(); + + return new_size; +} + +// Extract a single unsigned char from the binary data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +uint8_t DataExtractor::GetU8(offset_t *offset_ptr) const { + const uint8_t *data = static_cast<const uint8_t *>(GetData(offset_ptr, 1)); + if (data) + return *data; + return 0; +} + +// Extract "count" unsigned chars from the binary data and update the offset +// pointed to by "offset_ptr". The extracted data is copied into "dst". +// +// RETURNS the non-nullptr buffer pointer upon successful extraction of +// all the requested bytes, or nullptr when the data is not available in the +// buffer due to being out of bounds, or insufficient data. +void *DataExtractor::GetU8(offset_t *offset_ptr, void *dst, + uint32_t count) const { + const uint8_t *data = + static_cast<const uint8_t *>(GetData(offset_ptr, count)); + if (data) { + // Copy the data into the buffer + memcpy(dst, data, count); + // Return a non-nullptr pointer to the converted data as an indicator of + // success + return dst; + } + return nullptr; +} + +// Extract a single uint16_t from the data and update the offset pointed to by +// "offset_ptr". +// +// RETURNS the uint16_t that was extracted, or zero on failure. +uint16_t DataExtractor::GetU16(offset_t *offset_ptr) const { + uint16_t val = 0; + const uint8_t *data = + static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val))); + if (data) { + if (m_byte_order != endian::InlHostByteOrder()) + val = ReadSwapInt16(data); + else + val = ReadInt16(data); + } + return val; +} + +uint16_t DataExtractor::GetU16_unchecked(offset_t *offset_ptr) const { + uint16_t val; + if (m_byte_order == endian::InlHostByteOrder()) + val = ReadInt16(m_start, *offset_ptr); + else + val = ReadSwapInt16(m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint32_t DataExtractor::GetU32_unchecked(offset_t *offset_ptr) const { + uint32_t val; + if (m_byte_order == endian::InlHostByteOrder()) + val = ReadInt32(m_start, *offset_ptr); + else + val = ReadSwapInt32(m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint64_t DataExtractor::GetU64_unchecked(offset_t *offset_ptr) const { + uint64_t val; + if (m_byte_order == endian::InlHostByteOrder()) + val = ReadInt64(m_start, *offset_ptr); + else + val = ReadSwapInt64(m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +// Extract "count" uint16_t values from the binary data and update the offset +// pointed to by "offset_ptr". The extracted data is copied into "dst". +// +// RETURNS the non-nullptr buffer pointer upon successful extraction of +// all the requested bytes, or nullptr when the data is not available in the +// buffer due to being out of bounds, or insufficient data. +void *DataExtractor::GetU16(offset_t *offset_ptr, void *void_dst, + uint32_t count) const { + const size_t src_size = sizeof(uint16_t) * count; + const uint16_t *src = + static_cast<const uint16_t *>(GetData(offset_ptr, src_size)); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint16_t *dst_pos = static_cast<uint16_t *>(void_dst); + uint16_t *dst_end = dst_pos + count; + const uint16_t *src_pos = src; + while (dst_pos < dst_end) { + *dst_pos = ReadSwapInt16(src_pos); + ++dst_pos; + ++src_pos; + } + } else { + memcpy(void_dst, src, src_size); + } + // Return a non-nullptr pointer to the converted data as an indicator of + // success + return void_dst; + } + return nullptr; +} + +// Extract a single uint32_t from the data and update the offset pointed to by +// "offset_ptr". +// +// RETURNS the uint32_t that was extracted, or zero on failure. +uint32_t DataExtractor::GetU32(offset_t *offset_ptr) const { + uint32_t val = 0; + const uint8_t *data = + static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val))); + if (data) { + if (m_byte_order != endian::InlHostByteOrder()) { + val = ReadSwapInt32(data); + } else { + memcpy(&val, data, 4); + } + } + return val; +} + +// Extract "count" uint32_t values from the binary data and update the offset +// pointed to by "offset_ptr". The extracted data is copied into "dst". +// +// RETURNS the non-nullptr buffer pointer upon successful extraction of +// all the requested bytes, or nullptr when the data is not available in the +// buffer due to being out of bounds, or insufficient data. +void *DataExtractor::GetU32(offset_t *offset_ptr, void *void_dst, + uint32_t count) const { + const size_t src_size = sizeof(uint32_t) * count; + const uint32_t *src = + static_cast<const uint32_t *>(GetData(offset_ptr, src_size)); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint32_t *dst_pos = static_cast<uint32_t *>(void_dst); + uint32_t *dst_end = dst_pos + count; + const uint32_t *src_pos = src; + while (dst_pos < dst_end) { + *dst_pos = ReadSwapInt32(src_pos); + ++dst_pos; + ++src_pos; + } + } else { + memcpy(void_dst, src, src_size); + } + // Return a non-nullptr pointer to the converted data as an indicator of + // success + return void_dst; + } + return nullptr; +} + +// Extract a single uint64_t from the data and update the offset pointed to by +// "offset_ptr". +// +// RETURNS the uint64_t that was extracted, or zero on failure. +uint64_t DataExtractor::GetU64(offset_t *offset_ptr) const { + uint64_t val = 0; + const uint8_t *data = + static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val))); + if (data) { + if (m_byte_order != endian::InlHostByteOrder()) { + val = ReadSwapInt64(data); + } else { + memcpy(&val, data, 8); + } + } + return val; +} + +// GetU64 +// +// Get multiple consecutive 64 bit values. Return true if the entire read +// succeeds and increment the offset pointed to by offset_ptr, else return +// false and leave the offset pointed to by offset_ptr unchanged. +void *DataExtractor::GetU64(offset_t *offset_ptr, void *void_dst, + uint32_t count) const { + const size_t src_size = sizeof(uint64_t) * count; + const uint64_t *src = + static_cast<const uint64_t *>(GetData(offset_ptr, src_size)); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint64_t *dst_pos = static_cast<uint64_t *>(void_dst); + uint64_t *dst_end = dst_pos + count; + const uint64_t *src_pos = src; + while (dst_pos < dst_end) { + *dst_pos = ReadSwapInt64(src_pos); + ++dst_pos; + ++src_pos; + } + } else { + memcpy(void_dst, src, src_size); + } + // Return a non-nullptr pointer to the converted data as an indicator of + // success + return void_dst; + } + return nullptr; +} + +uint32_t DataExtractor::GetMaxU32(offset_t *offset_ptr, + size_t byte_size) const { + lldbassert(byte_size > 0 && byte_size <= 4 && "GetMaxU32 invalid byte_size!"); + return GetMaxU64(offset_ptr, byte_size); +} + +uint64_t DataExtractor::GetMaxU64(offset_t *offset_ptr, + size_t byte_size) const { + lldbassert(byte_size > 0 && byte_size <= 8 && "GetMaxU64 invalid byte_size!"); + switch (byte_size) { + case 1: + return GetU8(offset_ptr); + case 2: + return GetU16(offset_ptr); + case 4: + return GetU32(offset_ptr); + case 8: + return GetU64(offset_ptr); + default: { + // General case. + const uint8_t *data = + static_cast<const uint8_t *>(GetData(offset_ptr, byte_size)); + if (data == nullptr) + return 0; + return ReadMaxInt64(data, byte_size, m_byte_order); + } + } + return 0; +} + +uint64_t DataExtractor::GetMaxU64_unchecked(offset_t *offset_ptr, + size_t byte_size) const { + switch (byte_size) { + case 1: + return GetU8_unchecked(offset_ptr); + case 2: + return GetU16_unchecked(offset_ptr); + case 4: + return GetU32_unchecked(offset_ptr); + case 8: + return GetU64_unchecked(offset_ptr); + default: { + uint64_t res = ReadMaxInt64(&m_start[*offset_ptr], byte_size, m_byte_order); + *offset_ptr += byte_size; + return res; + } + } + return 0; +} + +int64_t DataExtractor::GetMaxS64(offset_t *offset_ptr, size_t byte_size) const { + uint64_t u64 = GetMaxU64(offset_ptr, byte_size); + return llvm::SignExtend64(u64, 8 * byte_size); +} + +uint64_t DataExtractor::GetMaxU64Bitfield(offset_t *offset_ptr, size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const { + assert(bitfield_bit_size <= 64); + uint64_t uval64 = GetMaxU64(offset_ptr, size); + + if (bitfield_bit_size == 0) + return uval64; + + int32_t lsbcount = bitfield_bit_offset; + if (m_byte_order == eByteOrderBig) + lsbcount = size * 8 - bitfield_bit_offset - bitfield_bit_size; + + if (lsbcount > 0) + uval64 >>= lsbcount; + + uint64_t bitfield_mask = + (bitfield_bit_size == 64 + ? std::numeric_limits<uint64_t>::max() + : ((static_cast<uint64_t>(1) << bitfield_bit_size) - 1)); + if (!bitfield_mask && bitfield_bit_offset == 0 && bitfield_bit_size == 64) + return uval64; + + uval64 &= bitfield_mask; + + return uval64; +} + +int64_t DataExtractor::GetMaxS64Bitfield(offset_t *offset_ptr, size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const { + assert(size >= 1 && "GetMaxS64Bitfield size must be >= 1"); + assert(size <= 8 && "GetMaxS64Bitfield size must be <= 8"); + int64_t sval64 = GetMaxS64(offset_ptr, size); + if (bitfield_bit_size == 0) + return sval64; + int32_t lsbcount = bitfield_bit_offset; + if (m_byte_order == eByteOrderBig) + lsbcount = size * 8 - bitfield_bit_offset - bitfield_bit_size; + if (lsbcount > 0) + sval64 >>= lsbcount; + uint64_t bitfield_mask = llvm::maskTrailingOnes<uint64_t>(bitfield_bit_size); + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & ((static_cast<uint64_t>(1)) << (bitfield_bit_size - 1))) + sval64 |= ~bitfield_mask; + return sval64; +} + +float DataExtractor::GetFloat(offset_t *offset_ptr) const { + return Get<float>(offset_ptr, 0.0f); +} + +double DataExtractor::GetDouble(offset_t *offset_ptr) const { + return Get<double>(offset_ptr, 0.0); +} + +long double DataExtractor::GetLongDouble(offset_t *offset_ptr) const { + long double val = 0.0; +#if defined(__i386__) || defined(__amd64__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64) + *offset_ptr += CopyByteOrderedData(*offset_ptr, 10, &val, sizeof(val), + endian::InlHostByteOrder()); +#else + *offset_ptr += CopyByteOrderedData(*offset_ptr, sizeof(val), &val, + sizeof(val), endian::InlHostByteOrder()); +#endif + return val; +} + +// Extract a single address from the data and update the offset pointed to by +// "offset_ptr". The size of the extracted address comes from the +// "this->m_addr_size" member variable and should be set correctly prior to +// extracting any address values. +// +// RETURNS the address that was extracted, or zero on failure. +uint64_t DataExtractor::GetAddress(offset_t *offset_ptr) const { + assert(m_addr_size >= 1 && m_addr_size <= 8); + return GetMaxU64(offset_ptr, m_addr_size); +} + +uint64_t DataExtractor::GetAddress_unchecked(offset_t *offset_ptr) const { + assert(m_addr_size >= 1 && m_addr_size <= 8); + return GetMaxU64_unchecked(offset_ptr, m_addr_size); +} + +size_t DataExtractor::ExtractBytes(offset_t offset, offset_t length, + ByteOrder dst_byte_order, void *dst) const { + const uint8_t *src = PeekData(offset, length); + if (src) { + if (dst_byte_order != GetByteOrder()) { + // Validate that only a word- or register-sized dst is byte swapped + assert(length == 1 || length == 2 || length == 4 || length == 8 || + length == 10 || length == 16 || length == 32); + + for (uint32_t i = 0; i < length; ++i) + (static_cast<uint8_t *>(dst))[i] = src[length - i - 1]; + } else + ::memcpy(dst, src, length); + return length; + } + return 0; +} + +// Extract data as it exists in target memory +lldb::offset_t DataExtractor::CopyData(offset_t offset, offset_t length, + void *dst) const { + const uint8_t *src = PeekData(offset, length); + if (src) { + ::memcpy(dst, src, length); + return length; + } + return 0; +} + +// Extract data and swap if needed when doing the copy +lldb::offset_t +DataExtractor::CopyByteOrderedData(offset_t src_offset, offset_t src_len, + void *dst_void_ptr, offset_t dst_len, + ByteOrder dst_byte_order) const { + // Validate the source info + if (!ValidOffsetForDataOfSize(src_offset, src_len)) + assert(ValidOffsetForDataOfSize(src_offset, src_len)); + assert(src_len > 0); + assert(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle); + + // Validate the destination info + assert(dst_void_ptr != nullptr); + assert(dst_len > 0); + assert(dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle); + + // Validate that only a word- or register-sized dst is byte swapped + assert(dst_byte_order == m_byte_order || dst_len == 1 || dst_len == 2 || + dst_len == 4 || dst_len == 8 || dst_len == 10 || dst_len == 16 || + dst_len == 32); + + // Must have valid byte orders set in this object and for destination + if (!(dst_byte_order == eByteOrderBig || + dst_byte_order == eByteOrderLittle) || + !(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle)) + return 0; + + uint8_t *dst = static_cast<uint8_t *>(dst_void_ptr); + const uint8_t *src = PeekData(src_offset, src_len); + if (src) { + if (dst_len >= src_len) { + // We are copying the entire value from src into dst. Calculate how many, + // if any, zeroes we need for the most significant bytes if "dst_len" is + // greater than "src_len"... + const size_t num_zeroes = dst_len - src_len; + if (dst_byte_order == eByteOrderBig) { + // Big endian, so we lead with zeroes... + if (num_zeroes > 0) + ::memset(dst, 0, num_zeroes); + // Then either copy or swap the rest + if (m_byte_order == eByteOrderBig) { + ::memcpy(dst + num_zeroes, src, src_len); + } else { + for (uint32_t i = 0; i < src_len; ++i) + dst[i + num_zeroes] = src[src_len - 1 - i]; + } + } else { + // Little endian destination, so we lead the value bytes + if (m_byte_order == eByteOrderBig) { + for (uint32_t i = 0; i < src_len; ++i) + dst[i] = src[src_len - 1 - i]; + } else { + ::memcpy(dst, src, src_len); + } + // And zero the rest... + if (num_zeroes > 0) + ::memset(dst + src_len, 0, num_zeroes); + } + return src_len; + } else { + // We are only copying some of the value from src into dst.. + + if (dst_byte_order == eByteOrderBig) { + // Big endian dst + if (m_byte_order == eByteOrderBig) { + // Big endian dst, with big endian src + ::memcpy(dst, src + (src_len - dst_len), dst_len); + } else { + // Big endian dst, with little endian src + for (uint32_t i = 0; i < dst_len; ++i) + dst[i] = src[dst_len - 1 - i]; + } + } else { + // Little endian dst + if (m_byte_order == eByteOrderBig) { + // Little endian dst, with big endian src + for (uint32_t i = 0; i < dst_len; ++i) + dst[i] = src[src_len - 1 - i]; + } else { + // Little endian dst, with big endian src + ::memcpy(dst, src, dst_len); + } + } + return dst_len; + } + } + return 0; +} + +// Extracts a variable length NULL terminated C string from the data at the +// offset pointed to by "offset_ptr". The "offset_ptr" will be updated with +// the offset of the byte that follows the NULL terminator byte. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if "length" is +// non-zero and there aren't enough available bytes, nullptr will be returned +// and "offset_ptr" will not be updated. +const char *DataExtractor::GetCStr(offset_t *offset_ptr) const { + const char *start = reinterpret_cast<const char *>(PeekData(*offset_ptr, 1)); + // Already at the end of the data. + if (!start) + return nullptr; + + const char *end = reinterpret_cast<const char *>(m_end); + + // Check all bytes for a null terminator that terminates a C string. + const char *terminator_or_end = std::find(start, end, '\0'); + + // We didn't find a null terminator, so return nullptr to indicate that there + // is no valid C string at that offset. + if (terminator_or_end == end) + return nullptr; + + // Update offset_ptr for the caller to point to the data behind the + // terminator (which is 1 byte long). + *offset_ptr += (terminator_or_end - start + 1UL); + return start; +} + +// Extracts a NULL terminated C string from the fixed length field of length +// "len" at the offset pointed to by "offset_ptr". The "offset_ptr" will be +// updated with the offset of the byte that follows the fixed length field. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if the offset +// plus the length of the field is out of bounds, or if the field does not +// contain a NULL terminator byte, nullptr will be returned and "offset_ptr" +// will not be updated. +const char *DataExtractor::GetCStr(offset_t *offset_ptr, offset_t len) const { + const char *cstr = reinterpret_cast<const char *>(PeekData(*offset_ptr, len)); + if (cstr != nullptr) { + if (memchr(cstr, '\0', len) == nullptr) { + return nullptr; + } + *offset_ptr += len; + return cstr; + } + return nullptr; +} + +// Peeks at a string in the contained data. No verification is done to make +// sure the entire string lies within the bounds of this object's data, only +// "offset" is verified to be a valid offset. +// +// Returns a valid C string pointer if "offset" is a valid offset in this +// object's data, else nullptr is returned. +const char *DataExtractor::PeekCStr(offset_t offset) const { + return reinterpret_cast<const char *>(PeekData(offset, 1)); +} + +// Extracts an unsigned LEB128 number from this object's data starting at the +// offset pointed to by "offset_ptr". The offset pointed to by "offset_ptr" +// will be updated with the offset of the byte following the last extracted +// byte. +// +// Returned the extracted integer value. +uint64_t DataExtractor::GetULEB128(offset_t *offset_ptr) const { + const uint8_t *src = PeekData(*offset_ptr, 1); + if (src == nullptr) + return 0; + + unsigned byte_count = 0; + uint64_t result = llvm::decodeULEB128(src, &byte_count, m_end); + *offset_ptr += byte_count; + return result; +} + +// Extracts an signed LEB128 number from this object's data starting at the +// offset pointed to by "offset_ptr". The offset pointed to by "offset_ptr" +// will be updated with the offset of the byte following the last extracted +// byte. +// +// Returned the extracted integer value. +int64_t DataExtractor::GetSLEB128(offset_t *offset_ptr) const { + const uint8_t *src = PeekData(*offset_ptr, 1); + if (src == nullptr) + return 0; + + unsigned byte_count = 0; + int64_t result = llvm::decodeSLEB128(src, &byte_count, m_end); + *offset_ptr += byte_count; + return result; +} + +// Skips a ULEB128 number (signed or unsigned) from this object's data starting +// at the offset pointed to by "offset_ptr". The offset pointed to by +// "offset_ptr" will be updated with the offset of the byte following the last +// extracted byte. +// +// Returns the number of bytes consumed during the extraction. +uint32_t DataExtractor::Skip_LEB128(offset_t *offset_ptr) const { + uint32_t bytes_consumed = 0; + const uint8_t *src = PeekData(*offset_ptr, 1); + if (src == nullptr) + return 0; + + const uint8_t *end = m_end; + + if (src < end) { + const uint8_t *src_pos = src; + while ((src_pos < end) && (*src_pos++ & 0x80)) + ++bytes_consumed; + *offset_ptr += src_pos - src; + } + return bytes_consumed; +} + +// Dumps bytes from this object's data to the stream "s" starting +// "start_offset" bytes into this data, and ending with the byte before +// "end_offset". "base_addr" will be added to the offset into the dumped data +// when showing the offset into the data in the output information. +// "num_per_line" objects of type "type" will be dumped with the option to +// override the format for each object with "type_format". "type_format" is a +// printf style formatting string. If "type_format" is nullptr, then an +// appropriate format string will be used for the supplied "type". If the +// stream "s" is nullptr, then the output will be send to Log(). +lldb::offset_t DataExtractor::PutToLog(Log *log, offset_t start_offset, + offset_t length, uint64_t base_addr, + uint32_t num_per_line, + DataExtractor::Type type) const { + if (log == nullptr) + return start_offset; + + offset_t offset; + offset_t end_offset; + uint32_t count; + StreamString sstr; + for (offset = start_offset, end_offset = offset + length, count = 0; + ValidOffset(offset) && offset < end_offset; ++count) { + if ((count % num_per_line) == 0) { + // Print out any previous string + if (sstr.GetSize() > 0) { + log->PutString(sstr.GetString()); + sstr.Clear(); + } + // Reset string offset and fill the current line string with address: + if (base_addr != LLDB_INVALID_ADDRESS) + sstr.Printf("0x%8.8" PRIx64 ":", + static_cast<uint64_t>(base_addr + (offset - start_offset))); + } + + switch (type) { + case TypeUInt8: + sstr.Printf(" %2.2x", GetU8(&offset)); + break; + case TypeChar: { + char ch = GetU8(&offset); + sstr.Printf(" %c", llvm::isPrint(ch) ? ch : ' '); + } break; + case TypeUInt16: + sstr.Printf(" %4.4x", GetU16(&offset)); + break; + case TypeUInt32: + sstr.Printf(" %8.8x", GetU32(&offset)); + break; + case TypeUInt64: + sstr.Printf(" %16.16" PRIx64, GetU64(&offset)); + break; + case TypePointer: + sstr.Printf(" 0x%" PRIx64, GetAddress(&offset)); + break; + case TypeULEB128: + sstr.Printf(" 0x%" PRIx64, GetULEB128(&offset)); + break; + case TypeSLEB128: + sstr.Printf(" %" PRId64, GetSLEB128(&offset)); + break; + } + } + + if (!sstr.Empty()) + log->PutString(sstr.GetString()); + + return offset; // Return the offset at which we ended up +} + +size_t DataExtractor::Copy(DataExtractor &dest_data) const { + if (m_data_sp) { + // we can pass along the SP to the data + dest_data.SetData(m_data_sp); + } else { + const uint8_t *base_ptr = m_start; + size_t data_size = GetByteSize(); + dest_data.SetData(DataBufferSP(new DataBufferHeap(base_ptr, data_size))); + } + return GetByteSize(); +} + +bool DataExtractor::Append(DataExtractor &rhs) { + if (rhs.GetByteOrder() != GetByteOrder()) + return false; + + if (rhs.GetByteSize() == 0) + return true; + + if (GetByteSize() == 0) + return (rhs.Copy(*this) > 0); + + size_t bytes = GetByteSize() + rhs.GetByteSize(); + + DataBufferHeap *buffer_heap_ptr = nullptr; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (!buffer_sp || buffer_heap_ptr == nullptr) + return false; + + uint8_t *bytes_ptr = buffer_heap_ptr->GetBytes(); + + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + memcpy(bytes_ptr + GetByteSize(), rhs.GetDataStart(), rhs.GetByteSize()); + + SetData(buffer_sp); + + return true; +} + +bool DataExtractor::Append(void *buf, offset_t length) { + if (buf == nullptr) + return false; + + if (length == 0) + return true; + + size_t bytes = GetByteSize() + length; + + DataBufferHeap *buffer_heap_ptr = nullptr; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (!buffer_sp || buffer_heap_ptr == nullptr) + return false; + + uint8_t *bytes_ptr = buffer_heap_ptr->GetBytes(); + + if (GetByteSize() > 0) + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + + memcpy(bytes_ptr + GetByteSize(), buf, length); + + SetData(buffer_sp); + + return true; +} + +void DataExtractor::Checksum(llvm::SmallVectorImpl<uint8_t> &dest, + uint64_t max_data) { + if (max_data == 0) + max_data = GetByteSize(); + else + max_data = std::min(max_data, GetByteSize()); + + llvm::MD5 md5; + + const llvm::ArrayRef<uint8_t> data(GetDataStart(), max_data); + md5.update(data); + + llvm::MD5::MD5Result result; + md5.final(result); + + dest.clear(); + dest.append(result.begin(), result.end()); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Diagnostics.cpp b/contrib/llvm-project/lldb/source/Utility/Diagnostics.cpp new file mode 100644 index 000000000000..b2a08165dd6c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Diagnostics.cpp @@ -0,0 +1,115 @@ +//===-- Diagnostics.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/Utility/Diagnostics.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +static constexpr size_t g_num_log_messages = 100; + +void Diagnostics::Initialize() { + lldbassert(!InstanceImpl() && "Already initialized."); + InstanceImpl().emplace(); +} + +void Diagnostics::Terminate() { + lldbassert(InstanceImpl() && "Already terminated."); + InstanceImpl().reset(); +} + +bool Diagnostics::Enabled() { return InstanceImpl().operator bool(); } + +std::optional<Diagnostics> &Diagnostics::InstanceImpl() { + static std::optional<Diagnostics> g_diagnostics; + return g_diagnostics; +} + +Diagnostics &Diagnostics::Instance() { return *InstanceImpl(); } + +Diagnostics::Diagnostics() : m_log_handler(g_num_log_messages) {} + +Diagnostics::~Diagnostics() {} + +Diagnostics::CallbackID Diagnostics::AddCallback(Callback callback) { + std::lock_guard<std::mutex> guard(m_callbacks_mutex); + CallbackID id = m_callback_id++; + m_callbacks.emplace_back(id, callback); + return id; +} + +void Diagnostics::RemoveCallback(CallbackID id) { + std::lock_guard<std::mutex> guard(m_callbacks_mutex); + llvm::erase_if(m_callbacks, + [id](const CallbackEntry &e) { return e.id == id; }); +} + +bool Diagnostics::Dump(raw_ostream &stream) { + Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory(); + if (!diagnostics_dir) { + stream << "unable to create diagnostic dir: " + << toString(diagnostics_dir.takeError()) << '\n'; + return false; + } + + return Dump(stream, *diagnostics_dir); +} + +bool Diagnostics::Dump(raw_ostream &stream, const FileSpec &dir) { + stream << "LLDB diagnostics will be written to " << dir.GetPath() << "\n"; + stream << "Please include the directory content when filing a bug report\n"; + + if (Error error = Create(dir)) { + stream << toString(std::move(error)) << '\n'; + return false; + } + + return true; +} + +llvm::Expected<FileSpec> Diagnostics::CreateUniqueDirectory() { + SmallString<128> diagnostics_dir; + std::error_code ec = + sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir); + if (ec) + return errorCodeToError(ec); + return FileSpec(diagnostics_dir.str()); +} + +Error Diagnostics::Create(const FileSpec &dir) { + if (Error err = DumpDiangosticsLog(dir)) + return err; + + for (CallbackEntry e : m_callbacks) { + if (Error err = e.callback(dir)) + return err; + } + + return Error::success(); +} + +llvm::Error Diagnostics::DumpDiangosticsLog(const FileSpec &dir) const { + FileSpec log_file = dir.CopyByAppendingPathComponent("diagnostics.log"); + std::error_code ec; + llvm::raw_fd_ostream stream(log_file.GetPath(), ec, llvm::sys::fs::OF_None); + if (ec) + return errorCodeToError(ec); + m_log_handler.Dump(stream); + return Error::success(); +} + +void Diagnostics::Report(llvm::StringRef message) { + m_log_handler.Emit(message); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Environment.cpp b/contrib/llvm-project/lldb/source/Utility/Environment.cpp new file mode 100644 index 000000000000..419e0745671a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Environment.cpp @@ -0,0 +1,49 @@ +//===-- Environment.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/Utility/Environment.h" + +using namespace lldb_private; + +char *Environment::Envp::make_entry(llvm::StringRef Key, + llvm::StringRef Value) { + const size_t size = Key.size() + 1 /*=*/ + Value.size() + 1 /*\0*/; + char *Result = static_cast<char *>( + Allocator.Allocate(sizeof(char) * size, alignof(char))); + char *Next = Result; + + Next = std::copy(Key.begin(), Key.end(), Next); + *Next++ = '='; + Next = std::copy(Value.begin(), Value.end(), Next); + *Next++ = '\0'; + + return Result; +} + +Environment::Envp::Envp(const Environment &Env) { + Data = static_cast<char **>( + Allocator.Allocate(sizeof(char *) * (Env.size() + 1), alignof(char *))); + char **Next = Data; + for (const auto &KV : Env) + *Next++ = make_entry(KV.first(), KV.second); + *Next++ = nullptr; +} + +Environment::Environment(const char *const *Env) { + if (!Env) + return; + while (*Env) + insert(*Env++); +} + +void Environment::insert(iterator first, iterator last) { + while (first != last) { + try_emplace(first->first(), first->second); + ++first; + } +} diff --git a/contrib/llvm-project/lldb/source/Utility/ErrorMessages.cpp b/contrib/llvm-project/lldb/source/Utility/ErrorMessages.cpp new file mode 100644 index 000000000000..aea5cb5f47c1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ErrorMessages.cpp @@ -0,0 +1,40 @@ +//===-- ErrorMessages.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/Utility/ErrorMessages.h" +#include "llvm/Support/ErrorHandling.h" + +namespace lldb_private { + +std::string toString(lldb::ExpressionResults e) { + switch (e) { + case lldb::eExpressionSetupError: + return "expression setup error"; + case lldb::eExpressionParseError: + return "expression parse error"; + case lldb::eExpressionResultUnavailable: + return "expression error"; + case lldb::eExpressionCompleted: + return "expression completed successfully"; + case lldb::eExpressionDiscarded: + return "expression discarded"; + case lldb::eExpressionInterrupted: + return "expression interrupted"; + case lldb::eExpressionHitBreakpoint: + return "expression hit breakpoint"; + case lldb::eExpressionTimedOut: + return "expression timed out"; + case lldb::eExpressionStoppedForDebug: + return "expression stop at entry point for debugging"; + case lldb::eExpressionThreadVanished: + return "expression thread vanished"; + } + llvm_unreachable("unhandled enumerator"); +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Utility/Event.cpp b/contrib/llvm-project/lldb/source/Utility/Event.cpp new file mode 100644 index 000000000000..5f431c0a6dd8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Event.cpp @@ -0,0 +1,275 @@ +//===-- Event.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/Utility/Event.h" + +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/StringExtras.h" + +#include <algorithm> + +#include <cctype> + +using namespace lldb; +using namespace lldb_private; + +#pragma mark - +#pragma mark Event + +// Event functions + +Event::Event(Broadcaster *broadcaster, uint32_t event_type, EventData *data) + : m_broadcaster_wp(broadcaster->GetBroadcasterImpl()), m_type(event_type), + m_data_sp(data) {} + +Event::Event(Broadcaster *broadcaster, uint32_t event_type, + const EventDataSP &event_data_sp) + : m_broadcaster_wp(broadcaster->GetBroadcasterImpl()), m_type(event_type), + m_data_sp(event_data_sp) {} + +Event::Event(uint32_t event_type, EventData *data) + : m_broadcaster_wp(), m_type(event_type), m_data_sp(data) {} + +Event::Event(uint32_t event_type, const EventDataSP &event_data_sp) + : m_broadcaster_wp(), m_type(event_type), m_data_sp(event_data_sp) {} + +Event::~Event() = default; + +void Event::Dump(Stream *s) const { + Broadcaster *broadcaster; + Broadcaster::BroadcasterImplSP broadcaster_impl_sp(m_broadcaster_wp.lock()); + if (broadcaster_impl_sp) + broadcaster = broadcaster_impl_sp->GetBroadcaster(); + else + broadcaster = nullptr; + + if (broadcaster) { + StreamString event_name; + if (broadcaster->GetEventNames(event_name, m_type, false)) + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x (%s), data = ", + static_cast<const void *>(this), + static_cast<void *>(broadcaster), + broadcaster->GetBroadcasterName().c_str(), m_type, + event_name.GetData()); + else + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x, data = ", + static_cast<const void *>(this), + static_cast<void *>(broadcaster), + broadcaster->GetBroadcasterName().c_str(), m_type); + } else + s->Printf("%p Event: broadcaster = NULL, type = 0x%8.8x, data = ", + static_cast<const void *>(this), m_type); + + if (m_data_sp) { + s->PutChar('{'); + m_data_sp->Dump(s); + s->PutChar('}'); + } else + s->Printf("<NULL>"); +} + +void Event::DoOnRemoval() { + std::lock_guard<std::mutex> guard(m_listeners_mutex); + + if (!m_data_sp) + return; + + m_data_sp->DoOnRemoval(this); + + // Now that the event has been handled by the primary event Listener, forward + // it to the other Listeners. + + EventSP me_sp = shared_from_this(); + if (m_data_sp->ForwardEventToPendingListeners(this)) { + for (auto listener_sp : m_pending_listeners) + listener_sp->AddEvent(me_sp); + m_pending_listeners.clear(); + } +} + +#pragma mark - +#pragma mark EventData + +// EventData functions + +EventData::EventData() = default; + +EventData::~EventData() = default; + +void EventData::Dump(Stream *s) const { s->PutCString("Generic Event Data"); } + +#pragma mark - +#pragma mark EventDataBytes + +// EventDataBytes functions + +EventDataBytes::EventDataBytes() : m_bytes() {} + +EventDataBytes::EventDataBytes(llvm::StringRef str) : m_bytes(str.str()) {} + +EventDataBytes::~EventDataBytes() = default; + +llvm::StringRef EventDataBytes::GetFlavorString() { return "EventDataBytes"; } + +llvm::StringRef EventDataBytes::GetFlavor() const { + return EventDataBytes::GetFlavorString(); +} + +void EventDataBytes::Dump(Stream *s) const { + if (llvm::all_of(m_bytes, llvm::isPrint)) + s->Format("\"{0}\"", m_bytes); + else + s->Format("{0:$[ ]@[x-2]}", llvm::make_range( + reinterpret_cast<const uint8_t *>(m_bytes.data()), + reinterpret_cast<const uint8_t *>(m_bytes.data() + + m_bytes.size()))); +} + +const void *EventDataBytes::GetBytes() const { + return (m_bytes.empty() ? nullptr : m_bytes.data()); +} + +size_t EventDataBytes::GetByteSize() const { return m_bytes.size(); } + +const void *EventDataBytes::GetBytesFromEvent(const Event *event_ptr) { + const EventDataBytes *e = GetEventDataFromEvent(event_ptr); + if (e != nullptr) + return e->GetBytes(); + return nullptr; +} + +size_t EventDataBytes::GetByteSizeFromEvent(const Event *event_ptr) { + const EventDataBytes *e = GetEventDataFromEvent(event_ptr); + if (e != nullptr) + return e->GetByteSize(); + return 0; +} + +const EventDataBytes * +EventDataBytes::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr != nullptr) { + const EventData *event_data = event_ptr->GetData(); + if (event_data && + event_data->GetFlavor() == EventDataBytes::GetFlavorString()) + return static_cast<const EventDataBytes *>(event_data); + } + return nullptr; +} + +llvm::StringRef EventDataReceipt::GetFlavorString() { + return "Process::ProcessEventData"; +} + +#pragma mark - +#pragma mark EventStructuredData + +// EventDataStructuredData definitions + +EventDataStructuredData::EventDataStructuredData() + : EventData(), m_process_sp(), m_object_sp(), m_plugin_sp() {} + +EventDataStructuredData::EventDataStructuredData( + const ProcessSP &process_sp, const StructuredData::ObjectSP &object_sp, + const lldb::StructuredDataPluginSP &plugin_sp) + : EventData(), m_process_sp(process_sp), m_object_sp(object_sp), + m_plugin_sp(plugin_sp) {} + +EventDataStructuredData::~EventDataStructuredData() = default; + +// EventDataStructuredData member functions + +llvm::StringRef EventDataStructuredData::GetFlavor() const { + return EventDataStructuredData::GetFlavorString(); +} + +void EventDataStructuredData::Dump(Stream *s) const { + if (!s) + return; + + if (m_object_sp) + m_object_sp->Dump(*s); +} + +const ProcessSP &EventDataStructuredData::GetProcess() const { + return m_process_sp; +} + +const StructuredData::ObjectSP &EventDataStructuredData::GetObject() const { + return m_object_sp; +} + +const lldb::StructuredDataPluginSP & +EventDataStructuredData::GetStructuredDataPlugin() const { + return m_plugin_sp; +} + +void EventDataStructuredData::SetProcess(const ProcessSP &process_sp) { + m_process_sp = process_sp; +} + +void EventDataStructuredData::SetObject( + const StructuredData::ObjectSP &object_sp) { + m_object_sp = object_sp; +} + +void EventDataStructuredData::SetStructuredDataPlugin( + const lldb::StructuredDataPluginSP &plugin_sp) { + m_plugin_sp = plugin_sp; +} + +// EventDataStructuredData static functions + +const EventDataStructuredData * +EventDataStructuredData::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr == nullptr) + return nullptr; + + const EventData *event_data = event_ptr->GetData(); + if (!event_data || + event_data->GetFlavor() != EventDataStructuredData::GetFlavorString()) + return nullptr; + + return static_cast<const EventDataStructuredData *>(event_data); +} + +ProcessSP EventDataStructuredData::GetProcessFromEvent(const Event *event_ptr) { + auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr); + if (event_data) + return event_data->GetProcess(); + else + return ProcessSP(); +} + +StructuredData::ObjectSP +EventDataStructuredData::GetObjectFromEvent(const Event *event_ptr) { + auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr); + if (event_data) + return event_data->GetObject(); + else + return StructuredData::ObjectSP(); +} + +lldb::StructuredDataPluginSP +EventDataStructuredData::GetPluginFromEvent(const Event *event_ptr) { + auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr); + if (event_data) + return event_data->GetStructuredDataPlugin(); + else + return StructuredDataPluginSP(); +} + +llvm::StringRef EventDataStructuredData::GetFlavorString() { + return "EventDataStructuredData"; +} diff --git a/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp b/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp new file mode 100644 index 000000000000..4bebbc9ff175 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp @@ -0,0 +1,575 @@ +//===-- FileSpec.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/Utility/FileSpec.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Triple.h" + +#include <algorithm> +#include <optional> +#include <system_error> +#include <vector> + +#include <cassert> +#include <climits> +#include <cstdio> +#include <cstring> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +static constexpr FileSpec::Style GetNativeStyle() { +#if defined(_WIN32) + return FileSpec::Style::windows; +#else + return FileSpec::Style::posix; +#endif +} + +bool PathStyleIsPosix(FileSpec::Style style) { + return llvm::sys::path::is_style_posix(style); +} + +const char *GetPathSeparators(FileSpec::Style style) { + return llvm::sys::path::get_separator(style).data(); +} + +char GetPreferredPathSeparator(FileSpec::Style style) { + return GetPathSeparators(style)[0]; +} + +void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) { + if (PathStyleIsPosix(style)) + return; + + std::replace(path.begin(), path.end(), '/', '\\'); +} + +} // end anonymous namespace + +FileSpec::FileSpec() : m_style(GetNativeStyle()) {} + +// Default constructor that can take an optional full path to a file on disk. +FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) { + SetFile(path, style); +} + +FileSpec::FileSpec(llvm::StringRef path, const llvm::Triple &triple) + : FileSpec{path, triple.isOSWindows() ? Style::windows : Style::posix} {} + +namespace { +/// Safely get a character at the specified index. +/// +/// \param[in] path +/// A full, partial, or relative path to a file. +/// +/// \param[in] i +/// An index into path which may or may not be valid. +/// +/// \return +/// The character at index \a i if the index is valid, or 0 if +/// the index is not valid. +inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) { + if (i < path.size()) + return path[i]; + return 0; +} + +/// Check if a path needs to be normalized. +/// +/// Check if a path needs to be normalized. We currently consider a +/// path to need normalization if any of the following are true +/// - path contains "/./" +/// - path contains "/../" +/// - path contains "//" +/// - path ends with "/" +/// Paths that start with "./" or with "../" are not considered to +/// need normalization since we aren't trying to resolve the path, +/// we are just trying to remove redundant things from the path. +/// +/// \param[in] path +/// A full, partial, or relative path to a file. +/// +/// \return +/// Returns \b true if the path needs to be normalized. +bool needsNormalization(const llvm::StringRef &path) { + if (path.empty()) + return false; + // We strip off leading "." values so these paths need to be normalized + if (path[0] == '.') + return true; + for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos; + i = path.find_first_of("\\/", i + 1)) { + const auto next = safeCharAtIndex(path, i+1); + switch (next) { + case 0: + // path separator char at the end of the string which should be + // stripped unless it is the one and only character + return i > 0; + case '/': + case '\\': + // two path separator chars in the middle of a path needs to be + // normalized + if (i > 0) + return true; + ++i; + break; + + case '.': { + const auto next_next = safeCharAtIndex(path, i+2); + switch (next_next) { + default: break; + case 0: return true; // ends with "/." + case '/': + case '\\': + return true; // contains "/./" + case '.': { + const auto next_next_next = safeCharAtIndex(path, i+3); + switch (next_next_next) { + default: break; + case 0: return true; // ends with "/.." + case '/': + case '\\': + return true; // contains "/../" + } + break; + } + } + } + break; + + default: + break; + } + } + return false; +} + + +} + +void FileSpec::SetFile(llvm::StringRef pathname) { SetFile(pathname, m_style); } + +// Update the contents of this object with a new path. The path will be split +// up into a directory and filename and stored as uniqued string values for +// quick comparison and efficient memory usage. +void FileSpec::SetFile(llvm::StringRef pathname, Style style) { + Clear(); + m_style = (style == Style::native) ? GetNativeStyle() : style; + + if (pathname.empty()) + return; + + llvm::SmallString<128> resolved(pathname); + + // Normalize the path by removing ".", ".." and other redundant components. + if (needsNormalization(resolved)) + llvm::sys::path::remove_dots(resolved, true, m_style); + + // Normalize back slashes to forward slashes + if (m_style == Style::windows) + std::replace(resolved.begin(), resolved.end(), '\\', '/'); + + if (resolved.empty()) { + // If we have no path after normalization set the path to the current + // directory. This matches what python does and also a few other path + // utilities. + m_filename.SetString("."); + return; + } + + // Split path into filename and directory. We rely on the underlying char + // pointer to be nullptr when the components are empty. + llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style); + if(!filename.empty()) + m_filename.SetString(filename); + + llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style); + if(!directory.empty()) + m_directory.SetString(directory); +} + +void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &triple) { + return SetFile(path, triple.isOSWindows() ? Style::windows : Style::posix); +} + +// Convert to pointer operator. This allows code to check any FileSpec objects +// to see if they contain anything valid using code such as: +// +// if (file_spec) +// {} +FileSpec::operator bool() const { return m_filename || m_directory; } + +// Logical NOT operator. This allows code to check any FileSpec objects to see +// if they are invalid using code such as: +// +// if (!file_spec) +// {} +bool FileSpec::operator!() const { return !m_directory && !m_filename; } + +bool FileSpec::DirectoryEquals(const FileSpec &rhs) const { + const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive(); + return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive); +} + +bool FileSpec::FileEquals(const FileSpec &rhs) const { + const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive(); + return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive); +} + +// Equal to operator +bool FileSpec::operator==(const FileSpec &rhs) const { + return FileEquals(rhs) && DirectoryEquals(rhs); +} + +// Not equal to operator +bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); } + +// Less than operator +bool FileSpec::operator<(const FileSpec &rhs) const { + return FileSpec::Compare(*this, rhs, true) < 0; +} + +// Dump a FileSpec object to a stream +Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) { + f.Dump(s.AsRawOstream()); + return s; +} + +// Clear this object by releasing both the directory and filename string values +// and making them both the empty string. +void FileSpec::Clear() { + m_directory.Clear(); + m_filename.Clear(); + PathWasModified(); +} + +// Compare two FileSpec objects. If "full" is true, then both the directory and +// the filename must match. If "full" is false, then the directory names for +// "a" and "b" are only compared if they are both non-empty. This allows a +// FileSpec object to only contain a filename and it can match FileSpec objects +// that have matching filenames with different paths. +// +// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if +// "a" is greater than "b". +int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) { + int result = 0; + + // case sensitivity of compare + const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); + + // If full is true, then we must compare both the directory and filename. + + // If full is false, then if either directory is empty, then we match on the + // basename only, and if both directories have valid values, we still do a + // full compare. This allows for matching when we just have a filename in one + // of the FileSpec objects. + + if (full || (a.m_directory && b.m_directory)) { + result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive); + if (result) + return result; + } + return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive); +} + +bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) { + if (full || (a.GetDirectory() && b.GetDirectory())) + return a == b; + + return a.FileEquals(b); +} + +bool FileSpec::Match(const FileSpec &pattern, const FileSpec &file) { + if (pattern.GetDirectory()) + return pattern == file; + if (pattern.GetFilename()) + return pattern.FileEquals(file); + return true; +} + +std::optional<FileSpec::Style> +FileSpec::GuessPathStyle(llvm::StringRef absolute_path) { + if (absolute_path.starts_with("/")) + return Style::posix; + if (absolute_path.starts_with(R"(\\)")) + return Style::windows; + if (absolute_path.size() >= 3 && llvm::isAlpha(absolute_path[0]) && + (absolute_path.substr(1, 2) == R"(:\)" || + absolute_path.substr(1, 2) == R"(:/)")) + return Style::windows; + return std::nullopt; +} + +// Dump the object to the supplied stream. If the object contains a valid +// directory name, it will be displayed followed by a directory delimiter, and +// the filename. +void FileSpec::Dump(llvm::raw_ostream &s) const { + std::string path{GetPath(true)}; + s << path; + char path_separator = GetPreferredPathSeparator(m_style); + if (!m_filename && !path.empty() && path.back() != path_separator) + s << path_separator; +} + +FileSpec::Style FileSpec::GetPathStyle() const { return m_style; } + +void FileSpec::SetDirectory(ConstString directory) { + m_directory = directory; + PathWasModified(); +} + +void FileSpec::SetDirectory(llvm::StringRef directory) { + m_directory = ConstString(directory); + PathWasModified(); +} + +void FileSpec::SetFilename(ConstString filename) { + m_filename = filename; + PathWasModified(); +} + +void FileSpec::SetFilename(llvm::StringRef filename) { + m_filename = ConstString(filename); + PathWasModified(); +} + +void FileSpec::ClearFilename() { + m_filename.Clear(); + PathWasModified(); +} + +void FileSpec::ClearDirectory() { + m_directory.Clear(); + PathWasModified(); +} + +// Extract the directory and path into a fixed buffer. This is needed as the +// directory and path are stored in separate string values. +size_t FileSpec::GetPath(char *path, size_t path_max_len, + bool denormalize) const { + if (!path) + return 0; + + std::string result = GetPath(denormalize); + ::snprintf(path, path_max_len, "%s", result.c_str()); + return std::min(path_max_len - 1, result.length()); +} + +std::string FileSpec::GetPath(bool denormalize) const { + llvm::SmallString<64> result; + GetPath(result, denormalize); + return static_cast<std::string>(result); +} + +ConstString FileSpec::GetPathAsConstString(bool denormalize) const { + return ConstString{GetPath(denormalize)}; +} + +void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, + bool denormalize) const { + path.append(m_directory.GetStringRef().begin(), + m_directory.GetStringRef().end()); + // Since the path was normalized and all paths use '/' when stored in these + // objects, we don't need to look for the actual syntax specific path + // separator, we just look for and insert '/'. + if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' && + m_filename.GetStringRef().back() != '/') + path.insert(path.end(), '/'); + path.append(m_filename.GetStringRef().begin(), + m_filename.GetStringRef().end()); + if (denormalize && !path.empty()) + Denormalize(path, m_style); +} + +llvm::StringRef FileSpec::GetFileNameExtension() const { + return llvm::sys::path::extension(m_filename.GetStringRef(), m_style); +} + +ConstString FileSpec::GetFileNameStrippingExtension() const { + return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style)); +} + +// Return the size in bytes that this object takes in memory. This returns the +// size in bytes of this object, not any shared string values it may refer to. +size_t FileSpec::MemorySize() const { + return m_filename.MemorySize() + m_directory.MemorySize(); +} + +FileSpec +FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const { + FileSpec ret = *this; + ret.AppendPathComponent(component); + return ret; +} + +FileSpec FileSpec::CopyByRemovingLastPathComponent() const { + llvm::SmallString<64> current_path; + GetPath(current_path, false); + if (llvm::sys::path::has_parent_path(current_path, m_style)) + return FileSpec(llvm::sys::path::parent_path(current_path, m_style), + m_style); + return *this; +} + +void FileSpec::PrependPathComponent(llvm::StringRef component) { + llvm::SmallString<64> new_path(component); + llvm::SmallString<64> current_path; + GetPath(current_path, false); + llvm::sys::path::append(new_path, + llvm::sys::path::begin(current_path, m_style), + llvm::sys::path::end(current_path), m_style); + SetFile(new_path, m_style); +} + +void FileSpec::PrependPathComponent(const FileSpec &new_path) { + return PrependPathComponent(new_path.GetPath(false)); +} + +void FileSpec::AppendPathComponent(llvm::StringRef component) { + llvm::SmallString<64> current_path; + GetPath(current_path, false); + llvm::sys::path::append(current_path, m_style, component); + SetFile(current_path, m_style); +} + +void FileSpec::AppendPathComponent(const FileSpec &new_path) { + return AppendPathComponent(new_path.GetPath(false)); +} + +bool FileSpec::RemoveLastPathComponent() { + llvm::SmallString<64> current_path; + GetPath(current_path, false); + if (llvm::sys::path::has_parent_path(current_path, m_style)) { + SetFile(llvm::sys::path::parent_path(current_path, m_style)); + return true; + } + return false; +} + +std::vector<llvm::StringRef> FileSpec::GetComponents() const { + std::vector<llvm::StringRef> components; + + auto dir_begin = llvm::sys::path::begin(m_directory.GetStringRef(), m_style); + auto dir_end = llvm::sys::path::end(m_directory.GetStringRef()); + + for (auto iter = dir_begin; iter != dir_end; ++iter) { + if (*iter == "/" || *iter == ".") + continue; + + components.push_back(*iter); + } + + if (!m_filename.IsEmpty() && m_filename != "/" && m_filename != ".") + components.push_back(m_filename.GetStringRef()); + + return components; +} + +/// Returns true if the filespec represents an implementation source +/// file (files with a ".c", ".cpp", ".m", ".mm" (many more) +/// extension). +/// +/// \return +/// \b true if the filespec represents an implementation source +/// file, \b false otherwise. +bool FileSpec::IsSourceImplementationFile() const { + llvm::StringRef extension = GetFileNameExtension(); + if (extension.empty()) + return false; + + static RegularExpression g_source_file_regex(llvm::StringRef( + "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|[" + "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO][" + "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])" + "$")); + return g_source_file_regex.Execute(extension); +} + +bool FileSpec::IsRelative() const { + return !IsAbsolute(); +} + +bool FileSpec::IsAbsolute() const { + // Check if we have cached if this path is absolute to avoid recalculating. + if (m_absolute != Absolute::Calculate) + return m_absolute == Absolute::Yes; + + m_absolute = Absolute::No; + + llvm::SmallString<64> path; + GetPath(path, false); + + if (!path.empty()) { + // We consider paths starting with ~ to be absolute. + if (path[0] == '~' || llvm::sys::path::is_absolute(path, m_style)) + m_absolute = Absolute::Yes; + } + + return m_absolute == Absolute::Yes; +} + +void FileSpec::MakeAbsolute(const FileSpec &dir) { + if (IsRelative()) + PrependPathComponent(dir); +} + +void llvm::format_provider<FileSpec>::format(const FileSpec &F, + raw_ostream &Stream, + StringRef Style) { + assert((Style.empty() || Style.equals_insensitive("F") || + Style.equals_insensitive("D")) && + "Invalid FileSpec style!"); + + StringRef dir = F.GetDirectory().GetStringRef(); + StringRef file = F.GetFilename().GetStringRef(); + + if (dir.empty() && file.empty()) { + Stream << "(empty)"; + return; + } + + if (Style.equals_insensitive("F")) { + Stream << (file.empty() ? "(empty)" : file); + return; + } + + // Style is either D or empty, either way we need to print the directory. + if (!dir.empty()) { + // Directory is stored in normalized form, which might be different than + // preferred form. In order to handle this, we need to cut off the + // filename, then denormalize, then write the entire denorm'ed directory. + llvm::SmallString<64> denormalized_dir = dir; + Denormalize(denormalized_dir, F.GetPathStyle()); + Stream << denormalized_dir; + Stream << GetPreferredPathSeparator(F.GetPathStyle()); + } + + if (Style.equals_insensitive("D")) { + // We only want to print the directory, so now just exit. + if (dir.empty()) + Stream << "(empty)"; + return; + } + + if (!file.empty()) + Stream << file; +} diff --git a/contrib/llvm-project/lldb/source/Utility/FileSpecList.cpp b/contrib/llvm-project/lldb/source/Utility/FileSpecList.cpp new file mode 100644 index 000000000000..7647e04a8204 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/FileSpecList.cpp @@ -0,0 +1,201 @@ +//===-- FileSpecList.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/Utility/FileSpecList.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Stream.h" + +#include <cstdint> +#include <utility> + +using namespace lldb_private; + +FileSpecList::FileSpecList() : m_files() {} + +FileSpecList::~FileSpecList() = default; + +// Append the "file_spec" to the end of the file spec list. +void FileSpecList::Append(const FileSpec &file_spec) { + m_files.push_back(file_spec); +} + +// Only append the "file_spec" if this list doesn't already contain it. +// +// Returns true if "file_spec" was added, false if this list already contained +// a copy of "file_spec". +bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) { + collection::iterator end = m_files.end(); + if (find(m_files.begin(), end, file_spec) == end) { + m_files.push_back(file_spec); + return true; + } + return false; +} + +// FIXME: Replace this with a DenseSet at the call site. It is inefficient. +bool SupportFileList::AppendIfUnique(const FileSpec &file_spec) { + collection::iterator end = m_files.end(); + if (find_if(m_files.begin(), end, + [&](const std::shared_ptr<SupportFile> &support_file) { + return support_file->GetSpecOnly() == file_spec; + }) == end) { + Append(file_spec); + return true; + } + return false; +} + +// Clears the file list. +void FileSpecList::Clear() { m_files.clear(); } + +// Dumps the file list to the supplied stream pointer "s". +void FileSpecList::Dump(Stream *s, const char *separator_cstr) const { + collection::const_iterator pos, end = m_files.end(); + for (pos = m_files.begin(); pos != end; ++pos) { + pos->Dump(s->AsRawOstream()); + if (separator_cstr && ((pos + 1) != end)) + s->PutCString(separator_cstr); + } +} + +// Find the index of the file in the file spec list that matches "file_spec" +// starting "start_idx" entries into the file spec list. +// +// Returns the valid index of the file that matches "file_spec" if it is found, +// else std::numeric_limits<uint32_t>::max() is returned. +static size_t FindFileIndex(size_t start_idx, const FileSpec &file_spec, + bool full, size_t num_files, + std::function<const FileSpec &(size_t)> get_ith) { + // When looking for files, we will compare only the filename if the FILE_SPEC + // argument is empty + bool compare_filename_only = file_spec.GetDirectory().IsEmpty(); + + for (size_t idx = start_idx; idx < num_files; ++idx) { + const FileSpec &ith = get_ith(idx); + if (compare_filename_only) { + if (ConstString::Equals(ith.GetFilename(), file_spec.GetFilename(), + file_spec.IsCaseSensitive() || + ith.IsCaseSensitive())) + return idx; + } else { + if (FileSpec::Equal(ith, file_spec, full)) + return idx; + } + } + + // We didn't find the file, return an invalid index + return UINT32_MAX; +} + +size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec, + bool full) const { + return ::FindFileIndex( + start_idx, file_spec, full, m_files.size(), + [&](size_t idx) -> const FileSpec & { return m_files[idx]; }); +} + +size_t SupportFileList::FindFileIndex(size_t start_idx, + const FileSpec &file_spec, + bool full) const { + return ::FindFileIndex(start_idx, file_spec, full, m_files.size(), + [&](size_t idx) -> const FileSpec & { + return m_files[idx]->GetSpecOnly(); + }); +} + +size_t SupportFileList::FindCompatibleIndex(size_t start_idx, + const FileSpec &file_spec) const { + const size_t num_files = m_files.size(); + if (start_idx >= num_files) + return UINT32_MAX; + + const bool file_spec_relative = file_spec.IsRelative(); + const bool file_spec_case_sensitive = file_spec.IsCaseSensitive(); + // When looking for files, we will compare only the filename if the directory + // argument is empty in file_spec + const bool full = !file_spec.GetDirectory().IsEmpty(); + + for (size_t idx = start_idx; idx < num_files; ++idx) { + const FileSpec &curr_file = m_files[idx]->GetSpecOnly(); + + // Always start by matching the filename first + if (!curr_file.FileEquals(file_spec)) + continue; + + // Only compare the full name if the we were asked to and if the current + // file entry has the a directory. If it doesn't have a directory then we + // only compare the filename. + if (FileSpec::Equal(curr_file, file_spec, full)) { + return idx; + } else if (curr_file.IsRelative() || file_spec_relative) { + llvm::StringRef curr_file_dir = curr_file.GetDirectory().GetStringRef(); + if (curr_file_dir.empty()) + return idx; // Basename match only for this file in the list + + // Check if we have a relative path in our file list, or if "file_spec" is + // relative, if so, check if either ends with the other. + llvm::StringRef file_spec_dir = file_spec.GetDirectory().GetStringRef(); + // We have a relative path in our file list, it matches if the + // specified path ends with this path, but we must ensure the full + // component matches (we don't want "foo/bar.cpp" to match "oo/bar.cpp"). + auto is_suffix = [](llvm::StringRef a, llvm::StringRef b, + bool case_sensitive) -> bool { + if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) + return a.empty() || a.ends_with("/"); + return false; + }; + const bool case_sensitive = + file_spec_case_sensitive || curr_file.IsCaseSensitive(); + if (is_suffix(curr_file_dir, file_spec_dir, case_sensitive) || + is_suffix(file_spec_dir, curr_file_dir, case_sensitive)) + return idx; + } + } + + // We didn't find the file, return an invalid index + return UINT32_MAX; +} +// Returns the FileSpec object at index "idx". If "idx" is out of range, then +// an empty FileSpec object will be returned. +const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const { + if (idx < m_files.size()) + return m_files[idx]; + static FileSpec g_empty_file_spec; + return g_empty_file_spec; +} + +const FileSpec &SupportFileList::GetFileSpecAtIndex(size_t idx) const { + if (idx < m_files.size()) + return m_files[idx]->Materialize(); + static FileSpec g_empty_file_spec; + return g_empty_file_spec; +} + +std::shared_ptr<SupportFile> +SupportFileList::GetSupportFileAtIndex(size_t idx) const { + if (idx < m_files.size()) + return m_files[idx]; + return {}; +} + +// Return the size in bytes that this object takes in memory. This returns the +// size in bytes of this object's member variables and any FileSpec objects its +// member variables contain, the result doesn't not include the string values +// for the directories any filenames as those are in shared string pools. +size_t FileSpecList::MemorySize() const { + size_t mem_size = sizeof(FileSpecList); + collection::const_iterator pos, end = m_files.end(); + for (pos = m_files.begin(); pos != end; ++pos) { + mem_size += pos->MemorySize(); + } + + return mem_size; +} + +// Return the number of files in the file spec list. +size_t FileSpecList::GetSize() const { return m_files.size(); } diff --git a/contrib/llvm-project/lldb/source/Utility/GDBRemote.cpp b/contrib/llvm-project/lldb/source/Utility/GDBRemote.cpp new file mode 100644 index 000000000000..276b1276f4e6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/GDBRemote.cpp @@ -0,0 +1,63 @@ +//===-- GDBRemote.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/Utility/GDBRemote.h" + +#include "lldb/Utility/Flags.h" +#include "lldb/Utility/Stream.h" + +#include <cstdio> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +StreamGDBRemote::StreamGDBRemote() : StreamString() {} + +StreamGDBRemote::StreamGDBRemote(uint32_t flags, uint32_t addr_size, + ByteOrder byte_order) + : StreamString(flags, addr_size, byte_order) {} + +StreamGDBRemote::~StreamGDBRemote() = default; + +int StreamGDBRemote::PutEscapedBytes(const void *s, size_t src_len) { + int bytes_written = 0; + const uint8_t *src = static_cast<const uint8_t *>(s); + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + while (src_len) { + uint8_t byte = *src; + src++; + src_len--; + if (byte == 0x23 || byte == 0x24 || byte == 0x7d || byte == 0x2a) { + bytes_written += PutChar(0x7d); + byte ^= 0x20; + } + bytes_written += PutChar(byte); + }; + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + +llvm::StringRef GDBRemotePacket::GetTypeStr() const { + switch (type) { + case GDBRemotePacket::ePacketTypeSend: + return "send"; + case GDBRemotePacket::ePacketTypeRecv: + return "read"; + case GDBRemotePacket::ePacketTypeInvalid: + return "invalid"; + } + llvm_unreachable("All enum cases should be handled"); +} + +void GDBRemotePacket::Dump(Stream &strm) const { + strm.Printf("tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n", tid, + bytes_transmitted, GetTypeStr().data(), packet.data.c_str()); +} diff --git a/contrib/llvm-project/lldb/source/Utility/IOObject.cpp b/contrib/llvm-project/lldb/source/Utility/IOObject.cpp new file mode 100644 index 000000000000..964edce0ce10 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/IOObject.cpp @@ -0,0 +1,14 @@ +//===-- IOObject.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/Utility/IOObject.h" + +using namespace lldb_private; + +const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1; +IOObject::~IOObject() = default; diff --git a/contrib/llvm-project/lldb/source/Utility/Instrumentation.cpp b/contrib/llvm-project/lldb/source/Utility/Instrumentation.cpp new file mode 100644 index 000000000000..581f657aea80 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Instrumentation.cpp @@ -0,0 +1,44 @@ +//===-- Instrumentation.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/Utility/Instrumentation.h" +#include "lldb/Utility/LLDBLog.h" +#include "llvm/Support/Signposts.h" + +#include <cstdio> +#include <cstdlib> +#include <limits> +#include <thread> + +using namespace lldb_private; +using namespace lldb_private::instrumentation; + +// Whether we're currently across the API boundary. +static thread_local bool g_global_boundary = false; + +// Instrument SB API calls with singposts when supported. +static llvm::ManagedStatic<llvm::SignpostEmitter> g_api_signposts; + +Instrumenter::Instrumenter(llvm::StringRef pretty_func, + std::string &&pretty_args) + : m_pretty_func(pretty_func) { + if (!g_global_boundary) { + g_global_boundary = true; + m_local_boundary = true; + g_api_signposts->startInterval(this, m_pretty_func); + } + LLDB_LOG(GetLog(LLDBLog::API), "[{0}] {1} ({2})", + m_local_boundary ? "external" : "internal", m_pretty_func, + pretty_args); +} + +Instrumenter::~Instrumenter() { + if (m_local_boundary) { + g_global_boundary = false; + g_api_signposts->endInterval(this, m_pretty_func); + } +} diff --git a/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp b/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp new file mode 100644 index 000000000000..4ecd6043e8ea --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp @@ -0,0 +1,66 @@ +//===-- LLDBAssert.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/Utility/LLDBAssert.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +#if LLVM_SUPPORT_XCODE_SIGNPOSTS +#include <os/log.h> +#endif + +#include <atomic> + +namespace lldb_private { + +static void DefaultAssertCallback(llvm::StringRef message, + llvm::StringRef backtrace, + llvm::StringRef prompt) { + llvm::errs() << message << '\n'; + llvm::errs() << backtrace; // Backtrace includes a newline. + llvm::errs() << prompt << '\n'; +} + +static std::atomic<LLDBAssertCallback> g_lldb_assert_callback = + &DefaultAssertCallback; + +void lldb_assert(bool expression, const char *expr_text, const char *func, + const char *file, unsigned int line) { + if (LLVM_LIKELY(expression)) + return; + +#if LLVM_SUPPORT_XCODE_SIGNPOSTS + if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) { + os_log_fault(OS_LOG_DEFAULT, + "Assertion failed: (%s), function %s, file %s, line %u\n", + expr_text, func, file, line); + } +#endif + + // Print a warning and encourage the user to file a bug report, similar to + // LLVM’s crash handler, and then return execution. + std::string buffer; + llvm::raw_string_ostream backtrace(buffer); + llvm::sys::PrintStackTrace(backtrace); + + (*g_lldb_assert_callback.load())( + llvm::formatv("Assertion failed: ({0}), function {1}, file {2}, line {3}", + expr_text, func, file, line) + .str(), + backtrace.str(), + "Please file a bug report against lldb reporting this failure log, and " + "as many details as possible"); +} + +void SetLLDBAssertCallback(LLDBAssertCallback callback) { + g_lldb_assert_callback.exchange(callback); +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Utility/LLDBLog.cpp b/contrib/llvm-project/lldb/source/Utility/LLDBLog.cpp new file mode 100644 index 000000000000..b193bd4eb07d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/LLDBLog.cpp @@ -0,0 +1,83 @@ +//===-- LLDBLog.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/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/ArrayRef.h" +#include <cstdarg> + +using namespace lldb_private; + +static constexpr Log::Category g_categories[] = { + {{"api"}, {"log API calls and return values"}, LLDBLog::API}, + {{"ast"}, {"log AST"}, LLDBLog::AST}, + {{"break"}, {"log breakpoints"}, LLDBLog::Breakpoints}, + {{"commands"}, {"log command argument parsing"}, LLDBLog::Commands}, + {{"comm"}, {"log communication activities"}, LLDBLog::Communication}, + {{"conn"}, {"log connection details"}, LLDBLog::Connection}, + {{"demangle"}, + {"log mangled names to catch demangler crashes"}, + LLDBLog::Demangle}, + {{"dyld"}, + {"log shared library related activities"}, + LLDBLog::DynamicLoader}, + {{"event"}, + {"log broadcaster, listener and event queue activities"}, + LLDBLog::Events}, + {{"expr"}, {"log expressions"}, LLDBLog::Expressions}, + {{"formatters"}, + {"log data formatters related activities"}, + LLDBLog::DataFormatters}, + {{"host"}, {"log host activities"}, LLDBLog::Host}, + {{"jit"}, {"log JIT events in the target"}, LLDBLog::JITLoader}, + {{"language"}, {"log language runtime events"}, LLDBLog::Language}, + {{"mmap"}, {"log mmap related activities"}, LLDBLog::MMap}, + {{"module"}, + {"log module activities such as when modules are created, destroyed, " + "replaced, and more"}, + LLDBLog::Modules}, + {{"object"}, + {"log object construction/destruction for important objects"}, + LLDBLog::Object}, + {{"os"}, {"log OperatingSystem plugin related activities"}, LLDBLog::OS}, + {{"platform"}, {"log platform events and activities"}, LLDBLog::Platform}, + {{"process"}, {"log process events and activities"}, LLDBLog::Process}, + {{"script"}, {"log events about the script interpreter"}, LLDBLog::Script}, + {{"state"}, + {"log private and public process state changes"}, + LLDBLog::State}, + {{"step"}, {"log step related activities"}, LLDBLog::Step}, + {{"symbol"}, {"log symbol related issues and warnings"}, LLDBLog::Symbols}, + {{"system-runtime"}, {"log system runtime events"}, LLDBLog::SystemRuntime}, + {{"target"}, {"log target events and activities"}, LLDBLog::Target}, + {{"temp"}, {"log internal temporary debug messages"}, LLDBLog::Temporary}, + {{"thread"}, {"log thread events and activities"}, LLDBLog::Thread}, + {{"types"}, {"log type system related activities"}, LLDBLog::Types}, + {{"unwind"}, {"log stack unwind activities"}, LLDBLog::Unwind}, + {{"watch"}, {"log watchpoint related activities"}, LLDBLog::Watchpoints}, + {{"on-demand"}, + {"log symbol on-demand related activities"}, + LLDBLog::OnDemand}, + {{"source"}, {"log source related activities"}, LLDBLog::Source}, +}; + +static Log::Channel g_log_channel(g_categories, + LLDBLog::Process | LLDBLog::Thread | + LLDBLog::DynamicLoader | + LLDBLog::Breakpoints | + LLDBLog::Watchpoints | LLDBLog::Step | + LLDBLog::State | LLDBLog::Symbols | + LLDBLog::Target | LLDBLog::Commands); + +template <> Log::Channel &lldb_private::LogChannelFor<LLDBLog>() { + return g_log_channel; +} + +void lldb_private::InitializeLldbChannel() { + Log::Register("lldb", g_log_channel); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Listener.cpp b/contrib/llvm-project/lldb/source/Utility/Listener.cpp new file mode 100644 index 000000000000..1efaad392502 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Listener.cpp @@ -0,0 +1,378 @@ +//===-- Listener.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/Utility/Listener.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/LLDBLog.h" + +#include <algorithm> +#include <memory> +#include <utility> + +using namespace lldb; +using namespace lldb_private; + +Listener::Listener(const char *name) : m_name(name) { + LLDB_LOGF(GetLog(LLDBLog::Object), "%p Listener::Listener('%s')", + static_cast<void *>(this), m_name.c_str()); +} + +Listener::~Listener() { + // Don't call Clear() from here as that can cause races. See #96750. + + LLDB_LOGF(GetLog(LLDBLog::Object), "%p Listener::%s('%s')", + static_cast<void *>(this), __FUNCTION__, m_name.c_str()); +} + +void Listener::Clear() { + Log *log = GetLog(LLDBLog::Object); + std::lock_guard<std::mutex> broadcasters_guard(m_broadcasters_mutex); + broadcaster_collection::iterator pos, end = m_broadcasters.end(); + for (pos = m_broadcasters.begin(); pos != end; ++pos) { + Broadcaster::BroadcasterImplSP broadcaster_sp(pos->first.lock()); + if (broadcaster_sp) + broadcaster_sp->RemoveListener(this, pos->second.event_mask); + } + m_broadcasters.clear(); + + std::lock_guard<std::mutex> events_guard(m_events_mutex); + m_events.clear(); + size_t num_managers = m_broadcaster_managers.size(); + + for (size_t i = 0; i < num_managers; i++) { + BroadcasterManagerSP manager_sp(m_broadcaster_managers[i].lock()); + if (manager_sp) + manager_sp->RemoveListener(this); + } + + LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this), + __FUNCTION__, m_name.c_str()); +} + +uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster, + uint32_t event_mask) { + if (broadcaster) { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + std::lock_guard<std::mutex> broadcasters_guard(m_broadcasters_mutex); + Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl()); + m_broadcasters.insert( + std::make_pair(impl_wp, BroadcasterInfo(event_mask))); + } + + uint32_t acquired_mask = + broadcaster->AddListener(this->shared_from_this(), event_mask); + + Log *log = GetLog(LLDBLog::Events); + if (log != nullptr) + LLDB_LOGF(log, + "%p Listener::StartListeningForEvents (broadcaster = %p, " + "mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s", + static_cast<void *>(this), static_cast<void *>(broadcaster), + event_mask, acquired_mask, m_name.c_str()); + + return acquired_mask; + } + return 0; +} + +uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster, + uint32_t event_mask, + HandleBroadcastCallback callback, + void *callback_user_data) { + if (broadcaster) { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + std::lock_guard<std::mutex> broadcasters_guard(m_broadcasters_mutex); + Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl()); + m_broadcasters.insert(std::make_pair( + impl_wp, BroadcasterInfo(event_mask, callback, callback_user_data))); + } + + uint32_t acquired_mask = + broadcaster->AddListener(this->shared_from_this(), event_mask); + + Log *log = GetLog(LLDBLog::Events); + if (log != nullptr) { + void **pointer = reinterpret_cast<void **>(&callback); + LLDB_LOGF(log, + "%p Listener::StartListeningForEvents (broadcaster = %p, " + "mask = 0x%8.8x, callback = %p, user_data = %p) " + "acquired_mask = 0x%8.8x for %s", + static_cast<void *>(this), static_cast<void *>(broadcaster), + event_mask, *pointer, static_cast<void *>(callback_user_data), + acquired_mask, m_name.c_str()); + } + + return acquired_mask; + } + return 0; +} + +bool Listener::StopListeningForEvents(Broadcaster *broadcaster, + uint32_t event_mask) { + if (broadcaster) { + // Scope for "locker" + { + std::lock_guard<std::mutex> broadcasters_guard(m_broadcasters_mutex); + m_broadcasters.erase(broadcaster->GetBroadcasterImpl()); + } + // Remove the broadcaster from our set of broadcasters + return broadcaster->RemoveListener(this->shared_from_this(), event_mask); + } + + return false; +} + +// Called when a Broadcaster is in its destructor. We need to remove all +// knowledge of this broadcaster and any events that it may have queued up +void Listener::BroadcasterWillDestruct(Broadcaster *broadcaster) { + // Scope for "broadcasters_locker" + { + std::lock_guard<std::mutex> broadcasters_guard(m_broadcasters_mutex); + m_broadcasters.erase(broadcaster->GetBroadcasterImpl()); + } + + // Scope for "event_locker" + { + std::lock_guard<std::mutex> events_guard(m_events_mutex); + // Remove all events for this broadcaster object. + event_collection::iterator pos = m_events.begin(); + while (pos != m_events.end()) { + if ((*pos)->GetBroadcaster() == broadcaster) + pos = m_events.erase(pos); + else + ++pos; + } + } +} + +void Listener::BroadcasterManagerWillDestruct(BroadcasterManagerSP manager_sp) { + const auto manager_matcher = + [&manager_sp](const BroadcasterManagerWP &input_wp) -> bool { + BroadcasterManagerSP input_sp = input_wp.lock(); + return (input_sp && input_sp == manager_sp); + }; + llvm::erase_if(m_broadcaster_managers, manager_matcher); +} + +void Listener::AddEvent(EventSP &event_sp) { + Log *log = GetLog(LLDBLog::Events); + if (log != nullptr) + LLDB_LOGF(log, "%p Listener('%s')::AddEvent (event_sp = {%p})", + static_cast<void *>(this), m_name.c_str(), + static_cast<void *>(event_sp.get())); + + std::lock_guard<std::mutex> guard(m_events_mutex); + m_events.push_back(event_sp); + m_events_condition.notify_all(); +} + +bool Listener::FindNextEventInternal( + std::unique_lock<std::mutex> &lock, + Broadcaster *broadcaster, // nullptr for any broadcaster + uint32_t event_type_mask, EventSP &event_sp, bool remove) { + // NOTE: callers of this function must lock m_events_mutex using a + // Mutex::Locker + // and pass the locker as the first argument. m_events_mutex is no longer + // recursive. + Log *log = GetLog(LLDBLog::Events); + + if (m_events.empty()) + return false; + + const auto event_matcher = + [broadcaster, event_type_mask](const EventSP &event_sp) -> bool { + if (broadcaster && !event_sp->BroadcasterIs(broadcaster)) + return false; + return event_type_mask == 0 || event_type_mask & event_sp->GetType(); + }; + Listener::event_collection::iterator pos = m_events.end(); + + if (broadcaster == nullptr && event_type_mask == 0) + pos = m_events.begin(); + else + pos = llvm::find_if(m_events, event_matcher); + + if (pos != m_events.end()) { + event_sp = *pos; + + if (log != nullptr) + LLDB_LOGF(log, + "%p '%s' Listener::FindNextEventInternal(broadcaster=%p, " + "event_type_mask=0x%8.8x, " + "remove=%i) event %p", + static_cast<void *>(this), GetName(), + static_cast<void *>(broadcaster), event_type_mask, remove, + static_cast<void *>(event_sp.get())); + + if (remove) { + m_events.erase(pos); + // Unlock the event queue here. We've removed this event and are about + // to return it so it should be okay to get the next event off the queue + // here - and it might be useful to do that in the "DoOnRemoval". + lock.unlock(); + event_sp->DoOnRemoval(); + } + return true; + } + + event_sp.reset(); + return false; +} + +Event *Listener::PeekAtNextEvent() { + std::unique_lock<std::mutex> guard(m_events_mutex); + EventSP event_sp; + if (FindNextEventInternal(guard, nullptr, 0, event_sp, false)) + return event_sp.get(); + return nullptr; +} + +Event *Listener::PeekAtNextEventForBroadcaster(Broadcaster *broadcaster) { + std::unique_lock<std::mutex> guard(m_events_mutex); + EventSP event_sp; + if (FindNextEventInternal(guard, broadcaster, 0, event_sp, false)) + return event_sp.get(); + return nullptr; +} + +Event * +Listener::PeekAtNextEventForBroadcasterWithType(Broadcaster *broadcaster, + uint32_t event_type_mask) { + std::unique_lock<std::mutex> guard(m_events_mutex); + EventSP event_sp; + if (FindNextEventInternal(guard, broadcaster, event_type_mask, event_sp, + false)) + return event_sp.get(); + return nullptr; +} + +bool Listener::GetEventInternal( + const Timeout<std::micro> &timeout, + Broadcaster *broadcaster, // nullptr for any broadcaster + uint32_t event_type_mask, EventSP &event_sp) { + Log *log = GetLog(LLDBLog::Events); + LLDB_LOG(log, "this = {0}, timeout = {1} for {2}", this, timeout, m_name); + + std::unique_lock<std::mutex> lock(m_events_mutex); + + while (true) { + if (FindNextEventInternal(lock, broadcaster, event_type_mask, event_sp, + true)) { + return true; + } else { + std::cv_status result = std::cv_status::no_timeout; + if (!timeout) + m_events_condition.wait(lock); + else + result = m_events_condition.wait_for(lock, *timeout); + + if (result == std::cv_status::timeout) { + log = GetLog(LLDBLog::Events); + LLDB_LOGF(log, "%p Listener::GetEventInternal() timed out for %s", + static_cast<void *>(this), m_name.c_str()); + return false; + } else if (result != std::cv_status::no_timeout) { + log = GetLog(LLDBLog::Events); + LLDB_LOGF(log, "%p Listener::GetEventInternal() unknown error for %s", + static_cast<void *>(this), m_name.c_str()); + return false; + } + } + } + + return false; +} + +bool Listener::GetEventForBroadcasterWithType( + Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp, + const Timeout<std::micro> &timeout) { + return GetEventInternal(timeout, broadcaster, event_type_mask, event_sp); +} + +bool Listener::GetEventForBroadcaster(Broadcaster *broadcaster, + EventSP &event_sp, + const Timeout<std::micro> &timeout) { + return GetEventInternal(timeout, broadcaster, 0, event_sp); +} + +bool Listener::GetEvent(EventSP &event_sp, const Timeout<std::micro> &timeout) { + return GetEventInternal(timeout, nullptr, 0, event_sp); +} + +size_t Listener::HandleBroadcastEvent(EventSP &event_sp) { + size_t num_handled = 0; + std::lock_guard<std::mutex> guard(m_broadcasters_mutex); + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + if (!broadcaster) + return 0; + broadcaster_collection::iterator pos; + broadcaster_collection::iterator end = m_broadcasters.end(); + Broadcaster::BroadcasterImplSP broadcaster_impl_sp( + broadcaster->GetBroadcasterImpl()); + for (pos = m_broadcasters.find(broadcaster_impl_sp); + pos != end && pos->first.lock() == broadcaster_impl_sp; ++pos) { + BroadcasterInfo info = pos->second; + if (event_sp->GetType() & info.event_mask) { + if (info.callback != nullptr) { + info.callback(event_sp, info.callback_user_data); + ++num_handled; + } + } + } + return num_handled; +} + +uint32_t +Listener::StartListeningForEventSpec(const BroadcasterManagerSP &manager_sp, + const BroadcastEventSpec &event_spec) { + if (!manager_sp) + return 0; + + const auto manager_matcher = + [&manager_sp](const BroadcasterManagerWP &input_wp) -> bool { + BroadcasterManagerSP input_sp = input_wp.lock(); + return (input_sp && input_sp == manager_sp); + }; + // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to + // avoid violating the lock hierarchy (manager before broadcasters). + std::lock_guard<std::mutex> manager_guard(manager_sp->m_manager_mutex); + std::lock_guard<std::mutex> guard(m_broadcasters_mutex); + + uint32_t bits_acquired = manager_sp->RegisterListenerForEventsNoLock( + this->shared_from_this(), event_spec); + if (bits_acquired) { + BroadcasterManagerWP manager_wp(manager_sp); + auto iter = llvm::find_if(m_broadcaster_managers, manager_matcher); + if (iter == m_broadcaster_managers.end()) + m_broadcaster_managers.push_back(manager_wp); + } + + return bits_acquired; +} + +bool Listener::StopListeningForEventSpec(const BroadcasterManagerSP &manager_sp, + const BroadcastEventSpec &event_spec) { + if (!manager_sp) + return false; + + // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to + // avoid violating the lock hierarchy (manager before broadcasters). + std::lock_guard<std::mutex> manager_guard(manager_sp->m_manager_mutex); + std::lock_guard<std::mutex> guard(m_broadcasters_mutex); + return manager_sp->UnregisterListenerForEventsNoLock(this->shared_from_this(), + event_spec); +} + +ListenerSP Listener::MakeListener(const char *name) { + return ListenerSP(new Listener(name)); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Log.cpp b/contrib/llvm-project/lldb/source/Utility/Log.cpp new file mode 100644 index 000000000000..6713a5bd7582 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Log.cpp @@ -0,0 +1,454 @@ +//===-- Log.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/Utility/Log.h" +#include "lldb/Utility/VASPrintf.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator.h" + +#include "llvm/Support/Casting.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#include <chrono> +#include <cstdarg> +#include <mutex> +#include <utility> + +#include <cassert> +#if defined(_WIN32) +#include <process.h> +#else +#include <unistd.h> +#endif + +using namespace lldb_private; + +char LogHandler::ID; +char StreamLogHandler::ID; +char CallbackLogHandler::ID; +char RotatingLogHandler::ID; +char TeeLogHandler::ID; + +llvm::ManagedStatic<Log::ChannelMap> Log::g_channel_map; + +void Log::ForEachCategory( + const Log::ChannelMap::value_type &entry, + llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) { + lambda("all", "all available logging categories"); + lambda("default", "default set of logging categories"); + for (const auto &category : entry.second.m_channel.categories) + lambda(category.name, category.description); +} + +void Log::ListCategories(llvm::raw_ostream &stream, + const ChannelMap::value_type &entry) { + stream << llvm::formatv("Logging categories for '{0}':\n", entry.first()); + ForEachCategory(entry, + [&stream](llvm::StringRef name, llvm::StringRef description) { + stream << llvm::formatv(" {0} - {1}\n", name, description); + }); +} + +Log::MaskType Log::GetFlags(llvm::raw_ostream &stream, + const ChannelMap::value_type &entry, + llvm::ArrayRef<const char *> categories) { + bool list_categories = false; + Log::MaskType flags = 0; + for (const char *category : categories) { + if (llvm::StringRef("all").equals_insensitive(category)) { + flags |= std::numeric_limits<Log::MaskType>::max(); + continue; + } + if (llvm::StringRef("default").equals_insensitive(category)) { + flags |= entry.second.m_channel.default_flags; + continue; + } + auto cat = llvm::find_if(entry.second.m_channel.categories, + [&](const Log::Category &c) { + return c.name.equals_insensitive(category); + }); + if (cat != entry.second.m_channel.categories.end()) { + flags |= cat->flag; + continue; + } + stream << llvm::formatv("error: unrecognized log category '{0}'\n", + category); + list_categories = true; + } + if (list_categories) + ListCategories(stream, entry); + return flags; +} + +void Log::Enable(const std::shared_ptr<LogHandler> &handler_sp, + uint32_t options, Log::MaskType flags) { + llvm::sys::ScopedWriter lock(m_mutex); + + MaskType mask = m_mask.fetch_or(flags, std::memory_order_relaxed); + if (mask | flags) { + m_options.store(options, std::memory_order_relaxed); + m_handler = handler_sp; + m_channel.log_ptr.store(this, std::memory_order_relaxed); + } +} + +void Log::Disable(Log::MaskType flags) { + llvm::sys::ScopedWriter lock(m_mutex); + + MaskType mask = m_mask.fetch_and(~flags, std::memory_order_relaxed); + if (!(mask & ~flags)) { + m_handler.reset(); + m_channel.log_ptr.store(nullptr, std::memory_order_relaxed); + } +} + +bool Log::Dump(llvm::raw_ostream &output_stream) { + llvm::sys::ScopedReader lock(m_mutex); + if (RotatingLogHandler *handler = + llvm::dyn_cast_or_null<RotatingLogHandler>(m_handler.get())) { + handler->Dump(output_stream); + return true; + } + return false; +} + +const Flags Log::GetOptions() const { + return m_options.load(std::memory_order_relaxed); +} + +Log::MaskType Log::GetMask() const { + return m_mask.load(std::memory_order_relaxed); +} + +void Log::PutCString(const char *cstr) { PutString(cstr); } + +void Log::PutString(llvm::StringRef str) { + std::string FinalMessage; + llvm::raw_string_ostream Stream(FinalMessage); + WriteHeader(Stream, "", ""); + Stream << str << "\n"; + WriteMessage(FinalMessage); +} + +// Simple variable argument logging with flags. +void Log::Printf(const char *format, ...) { + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); +} + +void Log::VAPrintf(const char *format, va_list args) { + llvm::SmallString<64> Content; + lldb_private::VASprintf(Content, format, args); + PutString(Content); +} + +void Log::Formatf(llvm::StringRef file, llvm::StringRef function, + const char *format, ...) { + va_list args; + va_start(args, format); + VAFormatf(file, function, format, args); + va_end(args); +} + +void Log::VAFormatf(llvm::StringRef file, llvm::StringRef function, + const char *format, va_list args) { + llvm::SmallString<64> Content; + lldb_private::VASprintf(Content, format, args); + Format(file, function, llvm::formatv("{0}", Content)); +} + +// Printing of errors that are not fatal. +void Log::Error(const char *format, ...) { + va_list args; + va_start(args, format); + VAError(format, args); + va_end(args); +} + +void Log::VAError(const char *format, va_list args) { + llvm::SmallString<64> Content; + VASprintf(Content, format, args); + + Printf("error: %s", Content.c_str()); +} + +// Printing of warnings that are not fatal only if verbose mode is enabled. +void Log::Verbose(const char *format, ...) { + if (!GetVerbose()) + return; + + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); +} + +// Printing of warnings that are not fatal. +void Log::Warning(const char *format, ...) { + llvm::SmallString<64> Content; + va_list args; + va_start(args, format); + VASprintf(Content, format, args); + va_end(args); + + Printf("warning: %s", Content.c_str()); +} + +void Log::Register(llvm::StringRef name, Channel &channel) { + auto iter = g_channel_map->try_emplace(name, channel); + assert(iter.second == true); + UNUSED_IF_ASSERT_DISABLED(iter); +} + +void Log::Unregister(llvm::StringRef name) { + auto iter = g_channel_map->find(name); + assert(iter != g_channel_map->end()); + iter->second.Disable(std::numeric_limits<MaskType>::max()); + g_channel_map->erase(iter); +} + +bool Log::EnableLogChannel(const std::shared_ptr<LogHandler> &log_handler_sp, + uint32_t log_options, llvm::StringRef channel, + llvm::ArrayRef<const char *> categories, + llvm::raw_ostream &error_stream) { + auto iter = g_channel_map->find(channel); + if (iter == g_channel_map->end()) { + error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel); + return false; + } + MaskType flags = categories.empty() + ? iter->second.m_channel.default_flags + : GetFlags(error_stream, *iter, categories); + iter->second.Enable(log_handler_sp, log_options, flags); + return true; +} + +bool Log::DisableLogChannel(llvm::StringRef channel, + llvm::ArrayRef<const char *> categories, + llvm::raw_ostream &error_stream) { + auto iter = g_channel_map->find(channel); + if (iter == g_channel_map->end()) { + error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel); + return false; + } + MaskType flags = categories.empty() + ? std::numeric_limits<MaskType>::max() + : GetFlags(error_stream, *iter, categories); + iter->second.Disable(flags); + return true; +} + +bool Log::DumpLogChannel(llvm::StringRef channel, + llvm::raw_ostream &output_stream, + llvm::raw_ostream &error_stream) { + auto iter = g_channel_map->find(channel); + if (iter == g_channel_map->end()) { + error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel); + return false; + } + if (!iter->second.Dump(output_stream)) { + error_stream << llvm::formatv( + "log channel '{0}' does not support dumping.\n", channel); + return false; + } + return true; +} + +bool Log::ListChannelCategories(llvm::StringRef channel, + llvm::raw_ostream &stream) { + auto ch = g_channel_map->find(channel); + if (ch == g_channel_map->end()) { + stream << llvm::formatv("Invalid log channel '{0}'.\n", channel); + return false; + } + ListCategories(stream, *ch); + return true; +} + +void Log::DisableAllLogChannels() { + for (auto &entry : *g_channel_map) + entry.second.Disable(std::numeric_limits<MaskType>::max()); +} + +void Log::ForEachChannelCategory( + llvm::StringRef channel, + llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) { + auto ch = g_channel_map->find(channel); + if (ch == g_channel_map->end()) + return; + + ForEachCategory(*ch, lambda); +} + +std::vector<llvm::StringRef> Log::ListChannels() { + std::vector<llvm::StringRef> result; + for (const auto &channel : *g_channel_map) + result.push_back(channel.first()); + return result; +} + +void Log::ListAllLogChannels(llvm::raw_ostream &stream) { + if (g_channel_map->empty()) { + stream << "No logging channels are currently registered.\n"; + return; + } + + for (const auto &channel : *g_channel_map) + ListCategories(stream, channel); +} + +bool Log::GetVerbose() const { + return m_options.load(std::memory_order_relaxed) & LLDB_LOG_OPTION_VERBOSE; +} + +void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file, + llvm::StringRef function) { + Flags options = GetOptions(); + static uint32_t g_sequence_id = 0; + // Add a sequence ID if requested + if (options.Test(LLDB_LOG_OPTION_PREPEND_SEQUENCE)) + OS << ++g_sequence_id << " "; + + // Timestamp if requested + if (options.Test(LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) { + auto now = std::chrono::duration<double>( + std::chrono::system_clock::now().time_since_epoch()); + OS << llvm::formatv("{0:f9} ", now.count()); + } + + // Add the process and thread if requested + if (options.Test(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) + OS << llvm::formatv("[{0,0+4}/{1,0+4}] ", getpid(), + llvm::get_threadid()); + + // Add the thread name if requested + if (options.Test(LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) { + llvm::SmallString<32> thread_name; + llvm::get_thread_name(thread_name); + + llvm::SmallString<12> format_str; + llvm::raw_svector_ostream format_os(format_str); + format_os << "{0,-" << llvm::alignTo<16>(thread_name.size()) << "} "; + OS << llvm::formatv(format_str.c_str(), thread_name); + } + + if (options.Test(LLDB_LOG_OPTION_BACKTRACE)) + llvm::sys::PrintStackTrace(OS); + + if (options.Test(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION) && + (!file.empty() || !function.empty())) { + file = llvm::sys::path::filename(file).take_front(40); + function = function.take_front(40); + OS << llvm::formatv("{0,-60:60} ", (file + ":" + function).str()); + } +} + +// If we have a callback registered, then we call the logging callback. If we +// have a valid file handle, we also log to the file. +void Log::WriteMessage(llvm::StringRef message) { + // Make a copy of our stream shared pointer in case someone disables our log + // while we are logging and releases the stream + auto handler_sp = GetHandler(); + if (!handler_sp) + return; + handler_sp->Emit(message); +} + +void Log::Format(llvm::StringRef file, llvm::StringRef function, + const llvm::formatv_object_base &payload) { + std::string message_string; + llvm::raw_string_ostream message(message_string); + WriteHeader(message, file, function); + message << payload << "\n"; + WriteMessage(message.str()); +} + +StreamLogHandler::StreamLogHandler(int fd, bool should_close, + size_t buffer_size) + : m_stream(fd, should_close, buffer_size == 0) { + if (buffer_size > 0) + m_stream.SetBufferSize(buffer_size); +} + +StreamLogHandler::~StreamLogHandler() { Flush(); } + +void StreamLogHandler::Flush() { + std::lock_guard<std::mutex> guard(m_mutex); + m_stream.flush(); +} + +void StreamLogHandler::Emit(llvm::StringRef message) { + if (m_stream.GetBufferSize() > 0) { + std::lock_guard<std::mutex> guard(m_mutex); + m_stream << message; + } else { + m_stream << message; + } +} + +CallbackLogHandler::CallbackLogHandler(lldb::LogOutputCallback callback, + void *baton) + : m_callback(callback), m_baton(baton) {} + +void CallbackLogHandler::Emit(llvm::StringRef message) { + m_callback(message.data(), m_baton); +} + +RotatingLogHandler::RotatingLogHandler(size_t size) + : m_messages(std::make_unique<std::string[]>(size)), m_size(size) {} + +void RotatingLogHandler::Emit(llvm::StringRef message) { + std::lock_guard<std::mutex> guard(m_mutex); + ++m_total_count; + const size_t index = m_next_index; + m_next_index = NormalizeIndex(index + 1); + m_messages[index] = message.str(); +} + +size_t RotatingLogHandler::NormalizeIndex(size_t i) const { return i % m_size; } + +size_t RotatingLogHandler::GetNumMessages() const { + return m_total_count < m_size ? m_total_count : m_size; +} + +size_t RotatingLogHandler::GetFirstMessageIndex() const { + return m_total_count < m_size ? 0 : m_next_index; +} + +void RotatingLogHandler::Dump(llvm::raw_ostream &stream) const { + std::lock_guard<std::mutex> guard(m_mutex); + const size_t start_idx = GetFirstMessageIndex(); + const size_t stop_idx = start_idx + GetNumMessages(); + for (size_t i = start_idx; i < stop_idx; ++i) { + const size_t idx = NormalizeIndex(i); + stream << m_messages[idx]; + } + stream.flush(); +} + +TeeLogHandler::TeeLogHandler(std::shared_ptr<LogHandler> first_log_handler, + std::shared_ptr<LogHandler> second_log_handler) + : m_first_log_handler(first_log_handler), + m_second_log_handler(second_log_handler) { + assert(m_first_log_handler && "first log handler must be valid"); + assert(m_second_log_handler && "second log handler must be valid"); +} + +void TeeLogHandler::Emit(llvm::StringRef message) { + m_first_log_handler->Emit(message); + m_second_log_handler->Emit(message); +} diff --git a/contrib/llvm-project/lldb/source/Utility/LoongArch_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/LoongArch_DWARF_Registers.h new file mode 100644 index 000000000000..34e40a066051 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/LoongArch_DWARF_Registers.h @@ -0,0 +1,177 @@ +//===-- LoongArch_DWARF_Registers.h -----------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_LOONGARCH_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_LOONGARCH_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace loongarch_dwarf { + +enum { + dwarf_gpr_r0 = 0, + dwarf_gpr_r1, + dwarf_gpr_r2, + dwarf_gpr_r3, + dwarf_gpr_r4, + dwarf_gpr_r5, + dwarf_gpr_r6, + dwarf_gpr_r7, + dwarf_gpr_r8, + dwarf_gpr_r9, + dwarf_gpr_r10, + dwarf_gpr_r11, + dwarf_gpr_r12, + dwarf_gpr_r13, + dwarf_gpr_r14, + dwarf_gpr_r15, + dwarf_gpr_r16, + dwarf_gpr_r17, + dwarf_gpr_r18, + dwarf_gpr_r19, + dwarf_gpr_r20, + dwarf_gpr_r21, + dwarf_gpr_r22, + dwarf_gpr_r23, + dwarf_gpr_r24, + dwarf_gpr_r25, + dwarf_gpr_r26, + dwarf_gpr_r27, + dwarf_gpr_r28, + dwarf_gpr_r29, + dwarf_gpr_r30, + dwarf_gpr_r31 = 31, + + dwarf_gpr_orig_a0, + dwarf_gpr_pc, + dwarf_gpr_badv, + + dwarf_gpr_reserved0 = 35, + dwarf_gpr_reserved1, + dwarf_gpr_reserved2, + dwarf_gpr_reserved3, + dwarf_gpr_reserved4, + dwarf_gpr_reserved5, + dwarf_gpr_reserved6, + dwarf_gpr_reserved7, + dwarf_gpr_reserved8, + dwarf_gpr_reserved9, + + dwarf_fpr_f0 = 45, + dwarf_fpr_f1, + dwarf_fpr_f2, + dwarf_fpr_f3, + dwarf_fpr_f4, + dwarf_fpr_f5, + dwarf_fpr_f6, + dwarf_fpr_f7, + dwarf_fpr_f8, + dwarf_fpr_f9, + dwarf_fpr_f10, + dwarf_fpr_f11, + dwarf_fpr_f12, + dwarf_fpr_f13, + dwarf_fpr_f14, + dwarf_fpr_f15, + dwarf_fpr_f16, + dwarf_fpr_f17, + dwarf_fpr_f18, + dwarf_fpr_f19, + dwarf_fpr_f20, + dwarf_fpr_f21, + dwarf_fpr_f22, + dwarf_fpr_f23, + dwarf_fpr_f24, + dwarf_fpr_f25, + dwarf_fpr_f26, + dwarf_fpr_f27, + dwarf_fpr_f28, + dwarf_fpr_f29, + dwarf_fpr_f30, + dwarf_fpr_f31 = 76, + + dwarf_fpr_fcc0, + dwarf_fpr_fcc1, + dwarf_fpr_fcc2, + dwarf_fpr_fcc3, + dwarf_fpr_fcc4, + dwarf_fpr_fcc5, + dwarf_fpr_fcc6, + dwarf_fpr_fcc7, + dwarf_fpr_fcsr, + + // register name alias + dwarf_gpr_zero = dwarf_gpr_r0, + dwarf_gpr_ra = dwarf_gpr_r1, + dwarf_gpr_tp = dwarf_gpr_r2, + dwarf_gpr_sp = dwarf_gpr_r3, + dwarf_gpr_a0 = dwarf_gpr_r4, + dwarf_gpr_a1 = dwarf_gpr_r5, + dwarf_gpr_a2 = dwarf_gpr_r6, + dwarf_gpr_a3 = dwarf_gpr_r7, + dwarf_gpr_a4 = dwarf_gpr_r8, + dwarf_gpr_a5 = dwarf_gpr_r9, + dwarf_gpr_a6 = dwarf_gpr_r10, + dwarf_gpr_a7 = dwarf_gpr_r11, + dwarf_gpr_t0 = dwarf_gpr_r12, + dwarf_gpr_t1 = dwarf_gpr_r13, + dwarf_gpr_t2 = dwarf_gpr_r14, + dwarf_gpr_t3 = dwarf_gpr_r15, + dwarf_gpr_t4 = dwarf_gpr_r16, + dwarf_gpr_t5 = dwarf_gpr_r17, + dwarf_gpr_t6 = dwarf_gpr_r18, + dwarf_gpr_t7 = dwarf_gpr_r19, + dwarf_gpr_t8 = dwarf_gpr_r20, + dwarf_gpr_fp = dwarf_gpr_r22, + dwarf_gpr_s0 = dwarf_gpr_r23, + dwarf_gpr_s1 = dwarf_gpr_r24, + dwarf_gpr_s2 = dwarf_gpr_r25, + dwarf_gpr_s3 = dwarf_gpr_r26, + dwarf_gpr_s4 = dwarf_gpr_r27, + dwarf_gpr_s5 = dwarf_gpr_r28, + dwarf_gpr_s6 = dwarf_gpr_r29, + dwarf_gpr_s7 = dwarf_gpr_r30, + dwarf_gpr_s8 = dwarf_gpr_r31, + + dwarf_fpr_fa0 = dwarf_fpr_f0, + dwarf_fpr_fa1 = dwarf_fpr_f1, + dwarf_fpr_fa2 = dwarf_fpr_f2, + dwarf_fpr_fa3 = dwarf_fpr_f3, + dwarf_fpr_fa4 = dwarf_fpr_f4, + dwarf_fpr_fa5 = dwarf_fpr_f5, + dwarf_fpr_fa6 = dwarf_fpr_f6, + dwarf_fpr_fa7 = dwarf_fpr_f7, + dwarf_fpr_ft0 = dwarf_fpr_f8, + dwarf_fpr_ft1 = dwarf_fpr_f9, + dwarf_fpr_ft2 = dwarf_fpr_f10, + dwarf_fpr_ft3 = dwarf_fpr_f11, + dwarf_fpr_ft4 = dwarf_fpr_f12, + dwarf_fpr_ft5 = dwarf_fpr_f13, + dwarf_fpr_ft6 = dwarf_fpr_f14, + dwarf_fpr_ft7 = dwarf_fpr_f15, + dwarf_fpr_ft8 = dwarf_fpr_f16, + dwarf_fpr_ft9 = dwarf_fpr_f17, + dwarf_fpr_ft10 = dwarf_fpr_f18, + dwarf_fpr_ft11 = dwarf_fpr_f19, + dwarf_fpr_ft12 = dwarf_fpr_f20, + dwarf_fpr_ft13 = dwarf_fpr_f21, + dwarf_fpr_ft14 = dwarf_fpr_f22, + dwarf_fpr_ft15 = dwarf_fpr_f23, + dwarf_fpr_fs0 = dwarf_fpr_f24, + dwarf_fpr_fs1 = dwarf_fpr_f25, + dwarf_fpr_fs2 = dwarf_fpr_f26, + dwarf_fpr_fs3 = dwarf_fpr_f27, + dwarf_fpr_fs4 = dwarf_fpr_f28, + dwarf_fpr_fs5 = dwarf_fpr_f29, + dwarf_fpr_fs6 = dwarf_fpr_f30, + dwarf_fpr_fs7 = dwarf_fpr_f31, +}; + +} // namespace loongarch_dwarf + +#endif // LLDB_SOURCE_UTILITY_LOONGARCH_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp b/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp new file mode 100644 index 000000000000..f002b86f163b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp @@ -0,0 +1,34 @@ +//===-- NameMatches.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/Utility/NameMatches.h" +#include "lldb/Utility/RegularExpression.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; + +bool lldb_private::NameMatches(llvm::StringRef name, NameMatch match_type, + llvm::StringRef match) { + switch (match_type) { + case NameMatch::Ignore: + return true; + case NameMatch::Equals: + return name == match; + case NameMatch::Contains: + return name.contains(match); + case NameMatch::StartsWith: + return name.starts_with(match); + case NameMatch::EndsWith: + return name.ends_with(match); + case NameMatch::RegularExpression: { + RegularExpression regex(match); + return regex.Execute(name); + } + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h new file mode 100644 index 000000000000..cd53e7a2abd0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h @@ -0,0 +1,193 @@ +//===-- PPC64LE_DWARF_Registers.h -------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_PPC64LE_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_PPC64LE_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace ppc64le_dwarf { + +enum { + dwarf_r0_ppc64le = 0, + dwarf_r1_ppc64le, + dwarf_r2_ppc64le, + dwarf_r3_ppc64le, + dwarf_r4_ppc64le, + dwarf_r5_ppc64le, + dwarf_r6_ppc64le, + dwarf_r7_ppc64le, + dwarf_r8_ppc64le, + dwarf_r9_ppc64le, + dwarf_r10_ppc64le, + dwarf_r11_ppc64le, + dwarf_r12_ppc64le, + dwarf_r13_ppc64le, + dwarf_r14_ppc64le, + dwarf_r15_ppc64le, + dwarf_r16_ppc64le, + dwarf_r17_ppc64le, + dwarf_r18_ppc64le, + dwarf_r19_ppc64le, + dwarf_r20_ppc64le, + dwarf_r21_ppc64le, + dwarf_r22_ppc64le, + dwarf_r23_ppc64le, + dwarf_r24_ppc64le, + dwarf_r25_ppc64le, + dwarf_r26_ppc64le, + dwarf_r27_ppc64le, + dwarf_r28_ppc64le, + dwarf_r29_ppc64le, + dwarf_r30_ppc64le, + dwarf_r31_ppc64le, + dwarf_f0_ppc64le, + dwarf_f1_ppc64le, + dwarf_f2_ppc64le, + dwarf_f3_ppc64le, + dwarf_f4_ppc64le, + dwarf_f5_ppc64le, + dwarf_f6_ppc64le, + dwarf_f7_ppc64le, + dwarf_f8_ppc64le, + dwarf_f9_ppc64le, + dwarf_f10_ppc64le, + dwarf_f11_ppc64le, + dwarf_f12_ppc64le, + dwarf_f13_ppc64le, + dwarf_f14_ppc64le, + dwarf_f15_ppc64le, + dwarf_f16_ppc64le, + dwarf_f17_ppc64le, + dwarf_f18_ppc64le, + dwarf_f19_ppc64le, + dwarf_f20_ppc64le, + dwarf_f21_ppc64le, + dwarf_f22_ppc64le, + dwarf_f23_ppc64le, + dwarf_f24_ppc64le, + dwarf_f25_ppc64le, + dwarf_f26_ppc64le, + dwarf_f27_ppc64le, + dwarf_f28_ppc64le, + dwarf_f29_ppc64le, + dwarf_f30_ppc64le, + dwarf_f31_ppc64le, + dwarf_lr_ppc64le = 65, + dwarf_ctr_ppc64le, + dwarf_cr_ppc64le = 68, + dwarf_xer_ppc64le = 76, + dwarf_vr0_ppc64le, + dwarf_vr1_ppc64le, + dwarf_vr2_ppc64le, + dwarf_vr3_ppc64le, + dwarf_vr4_ppc64le, + dwarf_vr5_ppc64le, + dwarf_vr6_ppc64le, + dwarf_vr7_ppc64le, + dwarf_vr8_ppc64le, + dwarf_vr9_ppc64le, + dwarf_vr10_ppc64le, + dwarf_vr11_ppc64le, + dwarf_vr12_ppc64le, + dwarf_vr13_ppc64le, + dwarf_vr14_ppc64le, + dwarf_vr15_ppc64le, + dwarf_vr16_ppc64le, + dwarf_vr17_ppc64le, + dwarf_vr18_ppc64le, + dwarf_vr19_ppc64le, + dwarf_vr20_ppc64le, + dwarf_vr21_ppc64le, + dwarf_vr22_ppc64le, + dwarf_vr23_ppc64le, + dwarf_vr24_ppc64le, + dwarf_vr25_ppc64le, + dwarf_vr26_ppc64le, + dwarf_vr27_ppc64le, + dwarf_vr28_ppc64le, + dwarf_vr29_ppc64le, + dwarf_vr30_ppc64le, + dwarf_vr31_ppc64le, + dwarf_vscr_ppc64le = 110, + dwarf_vrsave_ppc64le = 117, + dwarf_pc_ppc64le, + dwarf_softe_ppc64le, + dwarf_trap_ppc64le, + dwarf_origr3_ppc64le, + dwarf_fpscr_ppc64le, + dwarf_msr_ppc64le, + dwarf_vs0_ppc64le, + dwarf_vs1_ppc64le, + dwarf_vs2_ppc64le, + dwarf_vs3_ppc64le, + dwarf_vs4_ppc64le, + dwarf_vs5_ppc64le, + dwarf_vs6_ppc64le, + dwarf_vs7_ppc64le, + dwarf_vs8_ppc64le, + dwarf_vs9_ppc64le, + dwarf_vs10_ppc64le, + dwarf_vs11_ppc64le, + dwarf_vs12_ppc64le, + dwarf_vs13_ppc64le, + dwarf_vs14_ppc64le, + dwarf_vs15_ppc64le, + dwarf_vs16_ppc64le, + dwarf_vs17_ppc64le, + dwarf_vs18_ppc64le, + dwarf_vs19_ppc64le, + dwarf_vs20_ppc64le, + dwarf_vs21_ppc64le, + dwarf_vs22_ppc64le, + dwarf_vs23_ppc64le, + dwarf_vs24_ppc64le, + dwarf_vs25_ppc64le, + dwarf_vs26_ppc64le, + dwarf_vs27_ppc64le, + dwarf_vs28_ppc64le, + dwarf_vs29_ppc64le, + dwarf_vs30_ppc64le, + dwarf_vs31_ppc64le, + dwarf_vs32_ppc64le, + dwarf_vs33_ppc64le, + dwarf_vs34_ppc64le, + dwarf_vs35_ppc64le, + dwarf_vs36_ppc64le, + dwarf_vs37_ppc64le, + dwarf_vs38_ppc64le, + dwarf_vs39_ppc64le, + dwarf_vs40_ppc64le, + dwarf_vs41_ppc64le, + dwarf_vs42_ppc64le, + dwarf_vs43_ppc64le, + dwarf_vs44_ppc64le, + dwarf_vs45_ppc64le, + dwarf_vs46_ppc64le, + dwarf_vs47_ppc64le, + dwarf_vs48_ppc64le, + dwarf_vs49_ppc64le, + dwarf_vs50_ppc64le, + dwarf_vs51_ppc64le, + dwarf_vs52_ppc64le, + dwarf_vs53_ppc64le, + dwarf_vs54_ppc64le, + dwarf_vs55_ppc64le, + dwarf_vs56_ppc64le, + dwarf_vs57_ppc64le, + dwarf_vs58_ppc64le, + dwarf_vs59_ppc64le, + dwarf_vs60_ppc64le, + dwarf_vs61_ppc64le, + dwarf_vs62_ppc64le, + dwarf_vs63_ppc64le, +}; + +} // namespace ppc64le_dwarf + +#endif // LLDB_SOURCE_UTILITY_PPC64LE_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h new file mode 100644 index 000000000000..4f279be01b27 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h @@ -0,0 +1,126 @@ +//===-- PPC64_DWARF_Registers.h ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_PPC64_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_PPC64_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace ppc64_dwarf { + +enum { + dwarf_r0_ppc64 = 0, + dwarf_r1_ppc64, + dwarf_r2_ppc64, + dwarf_r3_ppc64, + dwarf_r4_ppc64, + dwarf_r5_ppc64, + dwarf_r6_ppc64, + dwarf_r7_ppc64, + dwarf_r8_ppc64, + dwarf_r9_ppc64, + dwarf_r10_ppc64, + dwarf_r11_ppc64, + dwarf_r12_ppc64, + dwarf_r13_ppc64, + dwarf_r14_ppc64, + dwarf_r15_ppc64, + dwarf_r16_ppc64, + dwarf_r17_ppc64, + dwarf_r18_ppc64, + dwarf_r19_ppc64, + dwarf_r20_ppc64, + dwarf_r21_ppc64, + dwarf_r22_ppc64, + dwarf_r23_ppc64, + dwarf_r24_ppc64, + dwarf_r25_ppc64, + dwarf_r26_ppc64, + dwarf_r27_ppc64, + dwarf_r28_ppc64, + dwarf_r29_ppc64, + dwarf_r30_ppc64, + dwarf_r31_ppc64, + dwarf_f0_ppc64, + dwarf_f1_ppc64, + dwarf_f2_ppc64, + dwarf_f3_ppc64, + dwarf_f4_ppc64, + dwarf_f5_ppc64, + dwarf_f6_ppc64, + dwarf_f7_ppc64, + dwarf_f8_ppc64, + dwarf_f9_ppc64, + dwarf_f10_ppc64, + dwarf_f11_ppc64, + dwarf_f12_ppc64, + dwarf_f13_ppc64, + dwarf_f14_ppc64, + dwarf_f15_ppc64, + dwarf_f16_ppc64, + dwarf_f17_ppc64, + dwarf_f18_ppc64, + dwarf_f19_ppc64, + dwarf_f20_ppc64, + dwarf_f21_ppc64, + dwarf_f22_ppc64, + dwarf_f23_ppc64, + dwarf_f24_ppc64, + dwarf_f25_ppc64, + dwarf_f26_ppc64, + dwarf_f27_ppc64, + dwarf_f28_ppc64, + dwarf_f29_ppc64, + dwarf_f30_ppc64, + dwarf_f31_ppc64, + dwarf_cr_ppc64 = 64, + dwarf_fpscr_ppc64, + dwarf_msr_ppc64, + dwarf_xer_ppc64 = 100, + dwarf_lr_ppc64 = 108, + dwarf_ctr_ppc64, + dwarf_vscr_ppc64, + dwarf_vrsave_ppc64 = 356, + dwarf_pc_ppc64, + dwarf_vr0_ppc64 = 1124, + dwarf_vr1_ppc64, + dwarf_vr2_ppc64, + dwarf_vr3_ppc64, + dwarf_vr4_ppc64, + dwarf_vr5_ppc64, + dwarf_vr6_ppc64, + dwarf_vr7_ppc64, + dwarf_vr8_ppc64, + dwarf_vr9_ppc64, + dwarf_vr10_ppc64, + dwarf_vr11_ppc64, + dwarf_vr12_ppc64, + dwarf_vr13_ppc64, + dwarf_vr14_ppc64, + dwarf_vr15_ppc64, + dwarf_vr16_ppc64, + dwarf_vr17_ppc64, + dwarf_vr18_ppc64, + dwarf_vr19_ppc64, + dwarf_vr20_ppc64, + dwarf_vr21_ppc64, + dwarf_vr22_ppc64, + dwarf_vr23_ppc64, + dwarf_vr24_ppc64, + dwarf_vr25_ppc64, + dwarf_vr26_ppc64, + dwarf_vr27_ppc64, + dwarf_vr28_ppc64, + dwarf_vr29_ppc64, + dwarf_vr30_ppc64, + dwarf_vr31_ppc64, +}; + +} // namespace ppc64_dwarf + +#endif // LLDB_SOURCE_UTILITY_PPC64_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp b/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp new file mode 100644 index 000000000000..845b337e246f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp @@ -0,0 +1,342 @@ +//===-- ProcessInfo.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/Utility/ProcessInfo.h" + +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UserIDResolver.h" +#include "llvm/ADT/SmallString.h" + +#include <climits> +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +ProcessInfo::ProcessInfo() + : m_executable(), m_arguments(), m_environment(), m_arch(), m_listener_sp(), + m_hijack_listener_sp(), m_shadow_listener_sp() {} + +ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch, + lldb::pid_t pid) + : m_executable(name), m_arguments(), m_environment(), m_arch(arch), + m_pid(pid), m_listener_sp(), m_hijack_listener_sp(), + m_shadow_listener_sp() {} + +void ProcessInfo::Clear() { + m_executable.Clear(); + m_arguments.Clear(); + m_environment.clear(); + m_uid = UINT32_MAX; + m_gid = UINT32_MAX; + m_arch.Clear(); + m_pid = LLDB_INVALID_PROCESS_ID; + m_scripted_metadata_sp.reset(); +} + +const char *ProcessInfo::GetName() const { + return m_executable.GetFilename().GetCString(); +} + +llvm::StringRef ProcessInfo::GetNameAsStringRef() const { + return m_executable.GetFilename().GetStringRef(); +} + +void ProcessInfo::Dump(Stream &s, Platform *platform) const { + s << "Executable: " << GetName() << "\n"; + s << "Triple: "; + m_arch.DumpTriple(s.AsRawOstream()); + s << "\n"; + + s << "Arguments:\n"; + m_arguments.Dump(s); + + s.Format("Environment:\n{0}", m_environment); +} + +void ProcessInfo::SetExecutableFile(const FileSpec &exe_file, + bool add_exe_file_as_first_arg) { + if (exe_file) { + m_executable = exe_file; + if (add_exe_file_as_first_arg) { + llvm::SmallString<128> filename; + exe_file.GetPath(filename); + if (!filename.empty()) + m_arguments.InsertArgumentAtIndex(0, filename); + } + } else { + m_executable.Clear(); + } +} + +llvm::StringRef ProcessInfo::GetArg0() const { return m_arg0; } + +void ProcessInfo::SetArg0(llvm::StringRef arg) { m_arg0 = std::string(arg); } + +void ProcessInfo::SetArguments(char const **argv, + bool first_arg_is_executable) { + m_arguments.SetArguments(argv); + + // Is the first argument the executable? + if (first_arg_is_executable) { + const char *first_arg = m_arguments.GetArgumentAtIndex(0); + if (first_arg) { + // Yes the first argument is an executable, set it as the executable in + // the launch options. Don't resolve the file path as the path could be a + // remote platform path + m_executable.SetFile(first_arg, FileSpec::Style::native); + } + } +} + +void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) { + // Copy all arguments + m_arguments = args; + + // Is the first argument the executable? + if (first_arg_is_executable) { + const char *first_arg = m_arguments.GetArgumentAtIndex(0); + if (first_arg) { + // Yes the first argument is an executable, set it as the executable in + // the launch options. Don't resolve the file path as the path could be a + // remote platform path + m_executable.SetFile(first_arg, FileSpec::Style::native); + } + } +} + +bool ProcessInfo::IsScriptedProcess() const { + return m_scripted_metadata_sp && *m_scripted_metadata_sp; +} + +void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const { + if (m_pid != LLDB_INVALID_PROCESS_ID) + s.Printf(" pid = %" PRIu64 "\n", m_pid); + + if (ParentProcessIDIsValid()) + s.Printf(" parent = %" PRIu64 "\n", GetParentProcessID()); + + if (m_executable) { + s.Printf(" name = %s\n", m_executable.GetFilename().GetCString()); + s.PutCString(" file = "); + m_executable.Dump(s.AsRawOstream()); + s.EOL(); + } + const uint32_t argc = m_arguments.GetArgumentCount(); + if (argc > 0) { + for (uint32_t i = 0; i < argc; i++) { + const char *arg = m_arguments.GetArgumentAtIndex(i); + if (i < 10) + s.Printf(" arg[%u] = %s\n", i, arg); + else + s.Printf("arg[%u] = %s\n", i, arg); + } + } + + s.Format("{0}", m_environment); + + if (m_arch.IsValid()) { + s.Printf(" arch = "); + m_arch.DumpTriple(s.AsRawOstream()); + s.EOL(); + } + + if (UserIDIsValid()) { + s.Format(" uid = {0,-5} ({1})\n", GetUserID(), + resolver.GetUserName(GetUserID()).value_or("")); + } + if (GroupIDIsValid()) { + s.Format(" gid = {0,-5} ({1})\n", GetGroupID(), + resolver.GetGroupName(GetGroupID()).value_or("")); + } + if (EffectiveUserIDIsValid()) { + s.Format(" euid = {0,-5} ({1})\n", GetEffectiveUserID(), + resolver.GetUserName(GetEffectiveUserID()).value_or("")); + } + if (EffectiveGroupIDIsValid()) { + s.Format(" egid = {0,-5} ({1})\n", GetEffectiveGroupID(), + resolver.GetGroupName(GetEffectiveGroupID()).value_or("")); + } +} + +void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args, + bool verbose) { + const char *label; + if (show_args || verbose) + label = "ARGUMENTS"; + else + label = "NAME"; + + if (verbose) { + s.Printf("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE " + " %s\n", + label); + s.PutCString( + "====== ====== ========== ========== ========== ========== " + "============================== ============================\n"); + } else { + s.Printf("PID PARENT USER TRIPLE %s\n", + label); + s.PutCString("====== ====== ========== ============================== " + "============================\n"); + } +} + +void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver, + bool show_args, bool verbose) const { + if (m_pid != LLDB_INVALID_PROCESS_ID) { + s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, + (ParentProcessIDIsValid()) ? GetParentProcessID() : 0); + + StreamString arch_strm; + if (m_arch.IsValid()) + m_arch.DumpTriple(arch_strm.AsRawOstream()); + + auto print = [&](bool (ProcessInstanceInfo::*isValid)() const, + uint32_t (ProcessInstanceInfo::*getID)() const, + std::optional<llvm::StringRef> (UserIDResolver::*getName)( + UserIDResolver::id_t id)) { + const char *format = "{0,-10} "; + if (!(this->*isValid)()) { + s.Format(format, ""); + return; + } + uint32_t id = (this->*getID)(); + if (auto name = (resolver.*getName)(id)) + s.Format(format, *name); + else + s.Format(format, id); + }; + if (verbose) { + print(&ProcessInstanceInfo::UserIDIsValid, + &ProcessInstanceInfo::GetUserID, &UserIDResolver::GetUserName); + print(&ProcessInstanceInfo::GroupIDIsValid, + &ProcessInstanceInfo::GetGroupID, &UserIDResolver::GetGroupName); + print(&ProcessInstanceInfo::EffectiveUserIDIsValid, + &ProcessInstanceInfo::GetEffectiveUserID, + &UserIDResolver::GetUserName); + print(&ProcessInstanceInfo::EffectiveGroupIDIsValid, + &ProcessInstanceInfo::GetEffectiveGroupID, + &UserIDResolver::GetGroupName); + + s.Printf("%-30s ", arch_strm.GetData()); + } else { + print(&ProcessInstanceInfo::EffectiveUserIDIsValid, + &ProcessInstanceInfo::GetEffectiveUserID, + &UserIDResolver::GetUserName); + s.Printf("%-30s ", arch_strm.GetData()); + } + + if (verbose || show_args) { + s.PutCString(m_arg0); + const uint32_t argc = m_arguments.GetArgumentCount(); + for (uint32_t i = 0; i < argc; i++) { + s.PutChar(' '); + s.PutCString(m_arguments.GetArgumentAtIndex(i)); + } + } else { + s.PutCString(GetName()); + } + + s.EOL(); + } +} + +bool ProcessInstanceInfoMatch::ArchitectureMatches( + const ArchSpec &arch_spec) const { + return !m_match_info.GetArchitecture().IsValid() || + m_match_info.GetArchitecture().IsCompatibleMatch(arch_spec); +} + +bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const { + if (m_name_match_type == NameMatch::Ignore) + return true; + const char *match_name = m_match_info.GetName(); + if (!match_name) + return true; + + return lldb_private::NameMatches(process_name, m_name_match_type, match_name); +} + +bool ProcessInstanceInfoMatch::ProcessIDsMatch( + const ProcessInstanceInfo &proc_info) const { + if (m_match_info.ProcessIDIsValid() && + m_match_info.GetProcessID() != proc_info.GetProcessID()) + return false; + + if (m_match_info.ParentProcessIDIsValid() && + m_match_info.GetParentProcessID() != proc_info.GetParentProcessID()) + return false; + return true; +} + +bool ProcessInstanceInfoMatch::UserIDsMatch( + const ProcessInstanceInfo &proc_info) const { + if (m_match_info.UserIDIsValid() && + m_match_info.GetUserID() != proc_info.GetUserID()) + return false; + + if (m_match_info.GroupIDIsValid() && + m_match_info.GetGroupID() != proc_info.GetGroupID()) + return false; + + if (m_match_info.EffectiveUserIDIsValid() && + m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID()) + return false; + + if (m_match_info.EffectiveGroupIDIsValid() && + m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID()) + return false; + return true; +} +bool ProcessInstanceInfoMatch::Matches( + const ProcessInstanceInfo &proc_info) const { + return ArchitectureMatches(proc_info.GetArchitecture()) && + ProcessIDsMatch(proc_info) && UserIDsMatch(proc_info) && + NameMatches(proc_info.GetName()); +} + +bool ProcessInstanceInfoMatch::MatchAllProcesses() const { + if (m_name_match_type != NameMatch::Ignore) + return false; + + if (m_match_info.ProcessIDIsValid()) + return false; + + if (m_match_info.ParentProcessIDIsValid()) + return false; + + if (m_match_info.UserIDIsValid()) + return false; + + if (m_match_info.GroupIDIsValid()) + return false; + + if (m_match_info.EffectiveUserIDIsValid()) + return false; + + if (m_match_info.EffectiveGroupIDIsValid()) + return false; + + if (m_match_info.GetArchitecture().IsValid()) + return false; + + if (m_match_all_users) + return false; + + return true; +} + +void ProcessInstanceInfoMatch::Clear() { + m_match_info.Clear(); + m_name_match_type = NameMatch::Ignore; + m_match_all_users = false; +} diff --git a/contrib/llvm-project/lldb/source/Utility/RISCV_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/RISCV_DWARF_Registers.h new file mode 100644 index 000000000000..8aed3d13f935 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/RISCV_DWARF_Registers.h @@ -0,0 +1,205 @@ +//===-- RISCV_DWARF_Registers.h ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_UTILITY_RISCV_DWARF_REGISTERS_H +#define LLDB_SOURCE_UTILITY_RISCV_DWARF_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace riscv_dwarf { + +enum { + dwarf_gpr_x0 = 0, + dwarf_gpr_x1, + dwarf_gpr_x2, + dwarf_gpr_x3, + dwarf_gpr_x4, + dwarf_gpr_x5, + dwarf_gpr_x6, + dwarf_gpr_x7, + dwarf_gpr_x8, + dwarf_gpr_x9, + dwarf_gpr_x10, + dwarf_gpr_x11, + dwarf_gpr_x12, + dwarf_gpr_x13, + dwarf_gpr_x14, + dwarf_gpr_x15, + dwarf_gpr_x16, + dwarf_gpr_x17, + dwarf_gpr_x18, + dwarf_gpr_x19, + dwarf_gpr_x20, + dwarf_gpr_x21, + dwarf_gpr_x22, + dwarf_gpr_x23, + dwarf_gpr_x24, + dwarf_gpr_x25, + dwarf_gpr_x26, + dwarf_gpr_x27, + dwarf_gpr_x28, + dwarf_gpr_x29, + dwarf_gpr_x30, + dwarf_gpr_x31 = 31, + + dwarf_fpr_f0 = 32, + dwarf_fpr_f1, + dwarf_fpr_f2, + dwarf_fpr_f3, + dwarf_fpr_f4, + dwarf_fpr_f5, + dwarf_fpr_f6, + dwarf_fpr_f7, + dwarf_fpr_f8, + dwarf_fpr_f9, + dwarf_fpr_f10, + dwarf_fpr_f11, + dwarf_fpr_f12, + dwarf_fpr_f13, + dwarf_fpr_f14, + dwarf_fpr_f15, + dwarf_fpr_f16, + dwarf_fpr_f17, + dwarf_fpr_f18, + dwarf_fpr_f19, + dwarf_fpr_f20, + dwarf_fpr_f21, + dwarf_fpr_f22, + dwarf_fpr_f23, + dwarf_fpr_f24, + dwarf_fpr_f25, + dwarf_fpr_f26, + dwarf_fpr_f27, + dwarf_fpr_f28, + dwarf_fpr_f29, + dwarf_fpr_f30, + dwarf_fpr_f31 = 63, + + // alternate frame return column + dwarf_alt_fr_col = 64, + + dwarf_vpr_v0 = 96, + dwarf_vpr_v1, + dwarf_vpr_v2, + dwarf_vpr_v3, + dwarf_vpr_v4, + dwarf_vpr_v5, + dwarf_vpr_v6, + dwarf_vpr_v7, + dwarf_vpr_v8, + dwarf_vpr_v9, + dwarf_vpr_v10, + dwarf_vpr_v11, + dwarf_vpr_v12, + dwarf_vpr_v13, + dwarf_vpr_v14, + dwarf_vpr_v15, + dwarf_vpr_v16, + dwarf_vpr_v17, + dwarf_vpr_v18, + dwarf_vpr_v19, + dwarf_vpr_v20, + dwarf_vpr_v21, + dwarf_vpr_v22, + dwarf_vpr_v23, + dwarf_vpr_v24, + dwarf_vpr_v25, + dwarf_vpr_v26, + dwarf_vpr_v27, + dwarf_vpr_v28, + dwarf_vpr_v29, + dwarf_vpr_v30, + dwarf_vpr_v31 = 127, + dwarf_first_csr = 4096, + dwarf_fpr_fcsr = dwarf_first_csr + 0x003, + // The vector extension adds seven unprivileged CSRs + // (vstart, vxsat, vxrm, vcsr, vtype, vl, vlenb) + // to a base scalar RISC-V ISA. + dwarf_vpr_vstart = dwarf_first_csr + 0x008, + dwarf_vpr_vxsat = dwarf_first_csr + 0x009, + dwarf_vpr_vxrm = dwarf_first_csr + 0x00A, + dwarf_vpr_vcsr = dwarf_first_csr + 0x00F, + dwarf_vpr_vl = dwarf_first_csr + 0xC20, + dwarf_vpr_vtype = dwarf_first_csr + 0xC21, + dwarf_vpr_vlenb = dwarf_first_csr + 0xC22, + dwarf_last_csr = 8191, + + // register ABI name + dwarf_gpr_zero = dwarf_gpr_x0, + dwarf_gpr_ra = dwarf_gpr_x1, + dwarf_gpr_sp = dwarf_gpr_x2, + dwarf_gpr_gp = dwarf_gpr_x3, + dwarf_gpr_tp = dwarf_gpr_x4, + dwarf_gpr_t0 = dwarf_gpr_x5, + dwarf_gpr_t1 = dwarf_gpr_x6, + dwarf_gpr_t2 = dwarf_gpr_x7, + dwarf_gpr_fp = dwarf_gpr_x8, + dwarf_gpr_s1 = dwarf_gpr_x9, + dwarf_gpr_a0 = dwarf_gpr_x10, + dwarf_gpr_a1 = dwarf_gpr_x11, + dwarf_gpr_a2 = dwarf_gpr_x12, + dwarf_gpr_a3 = dwarf_gpr_x13, + dwarf_gpr_a4 = dwarf_gpr_x14, + dwarf_gpr_a5 = dwarf_gpr_x15, + dwarf_gpr_a6 = dwarf_gpr_x16, + dwarf_gpr_a7 = dwarf_gpr_x17, + dwarf_gpr_s2 = dwarf_gpr_x18, + dwarf_gpr_s3 = dwarf_gpr_x19, + dwarf_gpr_s4 = dwarf_gpr_x20, + dwarf_gpr_s5 = dwarf_gpr_x21, + dwarf_gpr_s6 = dwarf_gpr_x22, + dwarf_gpr_s7 = dwarf_gpr_x23, + dwarf_gpr_s8 = dwarf_gpr_x24, + dwarf_gpr_s9 = dwarf_gpr_x25, + dwarf_gpr_s10 = dwarf_gpr_x26, + dwarf_gpr_s11 = dwarf_gpr_x27, + dwarf_gpr_t3 = dwarf_gpr_x28, + dwarf_gpr_t4 = dwarf_gpr_x29, + dwarf_gpr_t5 = dwarf_gpr_x30, + dwarf_gpr_t6 = dwarf_gpr_x31, + + dwarf_fpr_ft0 = dwarf_fpr_f0, + dwarf_fpr_ft1 = dwarf_fpr_f1, + dwarf_fpr_ft2 = dwarf_fpr_f2, + dwarf_fpr_ft3 = dwarf_fpr_f3, + dwarf_fpr_ft4 = dwarf_fpr_f4, + dwarf_fpr_ft5 = dwarf_fpr_f5, + dwarf_fpr_ft6 = dwarf_fpr_f6, + dwarf_fpr_ft7 = dwarf_fpr_f7, + dwarf_fpr_fs0 = dwarf_fpr_f8, + dwarf_fpr_fs1 = dwarf_fpr_f9, + dwarf_fpr_fa0 = dwarf_fpr_f10, + dwarf_fpr_fa1 = dwarf_fpr_f11, + dwarf_fpr_fa2 = dwarf_fpr_f12, + dwarf_fpr_fa3 = dwarf_fpr_f13, + dwarf_fpr_fa4 = dwarf_fpr_f14, + dwarf_fpr_fa5 = dwarf_fpr_f15, + dwarf_fpr_fa6 = dwarf_fpr_f16, + dwarf_fpr_fa7 = dwarf_fpr_f17, + dwarf_fpr_fs2 = dwarf_fpr_f18, + dwarf_fpr_fs3 = dwarf_fpr_f19, + dwarf_fpr_fs4 = dwarf_fpr_f20, + dwarf_fpr_fs5 = dwarf_fpr_f21, + dwarf_fpr_fs6 = dwarf_fpr_f22, + dwarf_fpr_fs7 = dwarf_fpr_f23, + dwarf_fpr_fs8 = dwarf_fpr_f24, + dwarf_fpr_fs9 = dwarf_fpr_f25, + dwarf_fpr_fs10 = dwarf_fpr_f26, + dwarf_fpr_fs11 = dwarf_fpr_f27, + dwarf_fpr_ft8 = dwarf_fpr_f28, + dwarf_fpr_ft9 = dwarf_fpr_f29, + dwarf_fpr_ft10 = dwarf_fpr_f30, + dwarf_fpr_ft11 = dwarf_fpr_f31, + + // mock pc regnum + dwarf_gpr_pc = 11451, +}; + +} // namespace riscv_dwarf + +#endif // LLDB_SOURCE_UTILITY_RISCV_DWARF_REGISTERS_H diff --git a/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp b/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp new file mode 100644 index 000000000000..cbf840258302 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp @@ -0,0 +1,843 @@ +//===-- RegisterValue.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/Utility/RegisterValue.h" + +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-defines.h" +#include "lldb/lldb-private-types.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +#include <cstdint> +#include <string> +#include <tuple> +#include <vector> + +#include <cassert> +#include <cinttypes> +#include <cstdio> + +using namespace lldb; +using namespace lldb_private; + +bool RegisterValue::GetData(DataExtractor &data) const { + return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0; +} + +uint32_t RegisterValue::GetAsMemoryData(const RegisterInfo ®_info, void *dst, + uint32_t dst_len, + lldb::ByteOrder dst_byte_order, + Status &error) const { + // ReadRegister should have already been called on this object prior to + // calling this. + if (GetType() == eTypeInvalid) { + // No value has been read into this object... + error.SetErrorStringWithFormat( + "invalid register value type for register %s", reg_info.name); + return 0; + } + + const uint32_t src_len = reg_info.byte_size; + + // Extract the register data into a data extractor + DataExtractor reg_data; + if (!GetData(reg_data)) { + error.SetErrorString("invalid register value to copy into"); + return 0; + } + + // Prepare a memory buffer that contains some or all of the register value + const uint32_t bytes_copied = + reg_data.CopyByteOrderedData(0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat( + "failed to copy data for register write of %s", reg_info.name); + + return bytes_copied; +} + +uint32_t RegisterValue::SetFromMemoryData(const RegisterInfo ®_info, + const void *src, uint32_t src_len, + lldb::ByteOrder src_byte_order, + Status &error) { + // Moving from addr into a register + // + // Case 1: src_len == dst_len + // + // |AABBCCDD| Address contents + // |AABBCCDD| Register contents + // + // Case 2: src_len > dst_len + // + // Status! (The register should always be big enough to hold the data) + // + // Case 3: src_len < dst_len + // + // |AABB| Address contents + // |AABB0000| Register contents [on little-endian hardware] + // |0000AABB| Register contents [on big-endian hardware] + const uint32_t dst_len = reg_info.byte_size; + + if (src_len > dst_len) { + error.SetErrorStringWithFormat( + "%u bytes is too big to store in register %s (%u bytes)", src_len, + reg_info.name, dst_len); + return 0; + } + + // Use a data extractor to correctly copy and pad the bytes read into the + // register value + DataExtractor src_data(src, src_len, src_byte_order, 4); + + error = SetValueFromData(reg_info, src_data, 0, true); + if (error.Fail()) + return 0; + + // If SetValueFromData succeeded, we must have copied all of src_len + return src_len; +} + +bool RegisterValue::GetScalarValue(Scalar &scalar) const { + switch (m_type) { + case eTypeInvalid: + break; + case eTypeBytes: { + DataExtractor data(buffer.bytes.data(), buffer.bytes.size(), + buffer.byte_order, 1); + if (scalar.SetValueFromData(data, lldb::eEncodingUint, buffer.bytes.size()) + .Success()) + return true; + } break; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + scalar = m_scalar; + return true; + } + return false; +} + +void RegisterValue::Clear() { m_type = eTypeInvalid; } + +RegisterValue::Type RegisterValue::SetType(const RegisterInfo ®_info) { + // To change the type, we simply copy the data in again, using the new format + RegisterValue copy; + DataExtractor copy_data; + if (copy.CopyValue(*this) && copy.GetData(copy_data)) { + Status error = SetValueFromData(reg_info, copy_data, 0, true); + assert(error.Success() && "Expected SetValueFromData to succeed."); + UNUSED_IF_ASSERT_DISABLED(error); + } + + return m_type; +} + +Status RegisterValue::SetValueFromData(const RegisterInfo ®_info, + DataExtractor &src, + lldb::offset_t src_offset, + bool partial_data_ok) { + Status error; + + if (src.GetByteSize() == 0) { + error.SetErrorString("empty data."); + return error; + } + + if (reg_info.byte_size == 0) { + error.SetErrorString("invalid register info."); + return error; + } + + uint32_t src_len = src.GetByteSize() - src_offset; + + if (!partial_data_ok && (src_len < reg_info.byte_size)) { + error.SetErrorString("not enough data."); + return error; + } + + // Cap the data length if there is more than enough bytes for this register + // value + if (src_len > reg_info.byte_size) + src_len = reg_info.byte_size; + + type128 int128; + + m_type = eTypeInvalid; + switch (reg_info.encoding) { + case eEncodingInvalid: + break; + case eEncodingUint: + case eEncodingSint: + if (reg_info.byte_size == 1) + SetUInt8(src.GetMaxU32(&src_offset, src_len)); + else if (reg_info.byte_size <= 2) + SetUInt16(src.GetMaxU32(&src_offset, src_len)); + else if (reg_info.byte_size <= 4) + SetUInt32(src.GetMaxU32(&src_offset, src_len)); + else if (reg_info.byte_size <= 8) + SetUInt64(src.GetMaxU64(&src_offset, src_len)); + else if (reg_info.byte_size <= 16) { + uint64_t data1 = src.GetU64(&src_offset); + uint64_t data2 = src.GetU64(&src_offset); + if (src.GetByteOrder() == eByteOrderBig) { + int128.x[0] = data1; + int128.x[1] = data2; + } else { + int128.x[0] = data2; + int128.x[1] = data1; + } + SetUInt128(llvm::APInt(128, 2, int128.x)); + } + break; + case eEncodingIEEE754: + if (reg_info.byte_size == sizeof(float)) + SetFloat(src.GetFloat(&src_offset)); + else if (reg_info.byte_size == sizeof(double)) + SetDouble(src.GetDouble(&src_offset)); + else if (reg_info.byte_size == sizeof(long double)) + SetLongDouble(src.GetLongDouble(&src_offset)); + break; + case eEncodingVector: { + m_type = eTypeBytes; + assert(reg_info.byte_size <= kMaxRegisterByteSize); + buffer.bytes.resize(reg_info.byte_size); + buffer.byte_order = src.GetByteOrder(); + if (src.CopyByteOrderedData( + src_offset, // offset within "src" to start extracting data + src_len, // src length + buffer.bytes.data(), // dst buffer + buffer.bytes.size(), // dst length + buffer.byte_order) == 0) // dst byte order + { + error.SetErrorStringWithFormat( + "failed to copy data for register write of %s", reg_info.name); + return error; + } + } + } + + if (m_type == eTypeInvalid) + error.SetErrorStringWithFormat( + "invalid register value type for register %s", reg_info.name); + return error; +} + +// Helper function for RegisterValue::SetValueFromString() +static bool ParseVectorEncoding(const RegisterInfo *reg_info, + llvm::StringRef vector_str, + const uint32_t byte_size, + RegisterValue *reg_value) { + // Example: vector_str = "{0x2c 0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a + // 0x2a 0x3e 0x84 0x4f 0x2a 0x3e}". + vector_str = vector_str.trim(); + vector_str.consume_front("{"); + vector_str.consume_back("}"); + vector_str = vector_str.trim(); + + char Sep = ' '; + + // The first split should give us: + // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f + // 0x2a 0x3e'). + llvm::StringRef car; + llvm::StringRef cdr = vector_str; + std::tie(car, cdr) = vector_str.split(Sep); + std::vector<uint8_t> bytes; + unsigned byte = 0; + + // Using radix auto-sensing by passing 0 as the radix. Keep on processing the + // vector elements as long as the parsing succeeds and the vector size is < + // byte_size. + while (!car.getAsInteger(0, byte) && bytes.size() < byte_size) { + bytes.push_back(byte); + std::tie(car, cdr) = cdr.split(Sep); + } + + // Check for vector of exact byte_size elements. + if (bytes.size() != byte_size) + return false; + + reg_value->SetBytes(&(bytes.front()), byte_size, eByteOrderLittle); + return true; +} + +static bool UInt64ValueIsValidForByteSize(uint64_t uval64, + size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = + (static_cast<uint64_t>(1) << static_cast<uint64_t>(total_byte_size * 8)) - + 1; + return uval64 <= max; +} + +static bool SInt64ValueIsValidForByteSize(int64_t sval64, + size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = (static_cast<int64_t>(1) + << static_cast<uint64_t>(total_byte_size * 8 - 1)) - + 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; +} + +Status RegisterValue::SetValueFromString(const RegisterInfo *reg_info, + llvm::StringRef value_str) { + Status error; + if (reg_info == nullptr) { + error.SetErrorString("Invalid register info argument."); + return error; + } + + m_type = eTypeInvalid; + if (value_str.empty()) { + error.SetErrorString("Invalid c-string value string."); + return error; + } + const uint32_t byte_size = reg_info->byte_size; + + uint64_t uval64; + int64_t ival64; + float flt_val; + double dbl_val; + long double ldbl_val; + switch (reg_info->encoding) { + case eEncodingInvalid: + error.SetErrorString("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size > sizeof(uint64_t)) { + error.SetErrorStringWithFormat( + "unsupported unsigned integer byte size: %u", byte_size); + break; + } + if (value_str.getAsInteger(0, uval64)) { + error.SetErrorStringWithFormatv( + "'{0}' is not a valid unsigned integer string value", value_str); + break; + } + + if (!UInt64ValueIsValidForByteSize(uval64, byte_size)) { + error.SetErrorStringWithFormat( + "value 0x%" PRIx64 + " is too large to fit in a %u byte unsigned integer value", + uval64, byte_size); + break; + } + + if (!SetUInt(uval64, reg_info->byte_size)) { + error.SetErrorStringWithFormat( + "unsupported unsigned integer byte size: %u", byte_size); + break; + } + break; + + case eEncodingSint: + if (byte_size > sizeof(long long)) { + error.SetErrorStringWithFormat("unsupported signed integer byte size: %u", + byte_size); + break; + } + + if (value_str.getAsInteger(0, ival64)) { + error.SetErrorStringWithFormatv( + "'{0}' is not a valid signed integer string value", value_str); + break; + } + + if (!SInt64ValueIsValidForByteSize(ival64, byte_size)) { + error.SetErrorStringWithFormat( + "value 0x%" PRIx64 + " is too large to fit in a %u byte signed integer value", + ival64, byte_size); + break; + } + + if (!SetUInt(ival64, reg_info->byte_size)) { + error.SetErrorStringWithFormat("unsupported signed integer byte size: %u", + byte_size); + break; + } + break; + + case eEncodingIEEE754: { + std::string value_string = std::string(value_str); + if (byte_size == sizeof(float)) { + if (::sscanf(value_string.c_str(), "%f", &flt_val) != 1) { + error.SetErrorStringWithFormat("'%s' is not a valid float string value", + value_string.c_str()); + break; + } + m_scalar = flt_val; + m_type = eTypeFloat; + } else if (byte_size == sizeof(double)) { + if (::sscanf(value_string.c_str(), "%lf", &dbl_val) != 1) { + error.SetErrorStringWithFormat("'%s' is not a valid float string value", + value_string.c_str()); + break; + } + m_scalar = dbl_val; + m_type = eTypeDouble; + } else if (byte_size == sizeof(long double)) { + if (::sscanf(value_string.c_str(), "%Lf", &ldbl_val) != 1) { + error.SetErrorStringWithFormat("'%s' is not a valid float string value", + value_string.c_str()); + break; + } + m_scalar = ldbl_val; + m_type = eTypeLongDouble; + } else { + error.SetErrorStringWithFormat("unsupported float byte size: %u", + byte_size); + return error; + } + break; + } + case eEncodingVector: + if (!ParseVectorEncoding(reg_info, value_str, byte_size, this)) + error.SetErrorString("unrecognized vector encoding string value."); + break; + } + + return error; +} + +bool RegisterValue::SignExtend(uint32_t sign_bitpos) { + switch (m_type) { + case eTypeInvalid: + break; + + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + return m_scalar.SignExtend(sign_bitpos); + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + case eTypeBytes: + break; + } + return false; +} + +bool RegisterValue::CopyValue(const RegisterValue &rhs) { + if (this == &rhs) + return rhs.m_type != eTypeInvalid; + + m_type = rhs.m_type; + switch (m_type) { + case eTypeInvalid: + return false; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + m_scalar = rhs.m_scalar; + break; + case eTypeBytes: + buffer.bytes = rhs.buffer.bytes; + buffer.byte_order = rhs.buffer.byte_order; + break; + } + return true; +} + +uint16_t RegisterValue::GetAsUInt16(uint16_t fail_value, + bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + + switch (m_type) { + default: + break; + case eTypeUInt8: + case eTypeUInt16: + return m_scalar.UShort(fail_value); + case eTypeBytes: { + switch (buffer.bytes.size()) { + default: + break; + case 1: + case 2: + return *reinterpret_cast<const uint16_t *>(buffer.bytes.data()); + } + } break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint32_t RegisterValue::GetAsUInt32(uint32_t fail_value, + bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.UInt(fail_value); + case eTypeBytes: { + switch (buffer.bytes.size()) { + default: + break; + case 1: + case 2: + case 4: + return *reinterpret_cast<const uint32_t *>(buffer.bytes.data()); + } + } break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint64_t RegisterValue::GetAsUInt64(uint64_t fail_value, + bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.ULongLong(fail_value); + case eTypeBytes: { + switch (buffer.bytes.size()) { + default: + break; + case 1: + return *(const uint8_t *)buffer.bytes.data(); + case 2: + return *reinterpret_cast<const uint16_t *>(buffer.bytes.data()); + case 4: + return *reinterpret_cast<const uint32_t *>(buffer.bytes.data()); + case 8: + return *reinterpret_cast<const uint64_t *>(buffer.bytes.data()); + } + } break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +llvm::APInt RegisterValue::GetAsUInt128(const llvm::APInt &fail_value, + bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.UInt128(fail_value); + case eTypeBytes: { + switch (buffer.bytes.size()) { + default: + break; + case 1: + case 2: + case 4: + case 8: + case 16: + return llvm::APInt( + BITWIDTH_INT128, NUM_OF_WORDS_INT128, + (reinterpret_cast<const type128 *>(buffer.bytes.data()))->x); + } + } break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +float RegisterValue::GetAsFloat(float fail_value, bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.Float(fail_value); + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +double RegisterValue::GetAsDouble(double fail_value, bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.Double(fail_value); + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +long double RegisterValue::GetAsLongDouble(long double fail_value, + bool *success_ptr) const { + if (success_ptr) + *success_ptr = true; + switch (m_type) { + default: + break; + + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.LongDouble(); + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +const void *RegisterValue::GetBytes() const { + switch (m_type) { + case eTypeInvalid: + break; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + m_scalar.GetBytes(buffer.bytes); + return buffer.bytes.data(); + case eTypeBytes: + return buffer.bytes.data(); + } + return nullptr; +} + +uint32_t RegisterValue::GetByteSize() const { + switch (m_type) { + case eTypeInvalid: + break; + case eTypeUInt8: + return 1; + case eTypeUInt16: + return 2; + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar.GetByteSize(); + case eTypeBytes: + return buffer.bytes.size(); + } + return 0; +} + +bool RegisterValue::SetUInt(uint64_t uint, uint32_t byte_size) { + if (byte_size == 0) { + SetUInt64(uint); + } else if (byte_size == 1) { + SetUInt8(uint); + } else if (byte_size <= 2) { + SetUInt16(uint); + } else if (byte_size <= 4) { + SetUInt32(uint); + } else if (byte_size <= 8) { + SetUInt64(uint); + } else if (byte_size <= 16) { + SetUInt128(llvm::APInt(128, uint)); + } else + return false; + return true; +} + +void RegisterValue::SetBytes(const void *bytes, size_t length, + lldb::ByteOrder byte_order) { + if (bytes && length > 0) { + m_type = eTypeBytes; + buffer.bytes.resize(length); + memcpy(buffer.bytes.data(), bytes, length); + buffer.byte_order = byte_order; + } else { + m_type = eTypeInvalid; + buffer.bytes.resize(0); + } +} + +bool RegisterValue::operator==(const RegisterValue &rhs) const { + if (m_type == rhs.m_type) { + switch (m_type) { + case eTypeInvalid: + return true; + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + return m_scalar == rhs.m_scalar; + case eTypeBytes: + return buffer.bytes == rhs.buffer.bytes; + } + } + return false; +} + +bool RegisterValue::operator!=(const RegisterValue &rhs) const { + return !(*this == rhs); +} + +bool RegisterValue::ClearBit(uint32_t bit) { + switch (m_type) { + case eTypeInvalid: + break; + + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + if (bit < (GetByteSize() * 8)) { + return m_scalar.ClearBit(bit); + } + break; + + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (buffer.byte_order == eByteOrderBig || + buffer.byte_order == eByteOrderLittle) { + uint32_t byte_idx; + if (buffer.byte_order == eByteOrderBig) + byte_idx = buffer.bytes.size() - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < buffer.bytes.size()) { + buffer.bytes[byte_idx] &= ~(1u << byte_bit); + return true; + } + } + break; + } + return false; +} + +bool RegisterValue::SetBit(uint32_t bit) { + switch (m_type) { + case eTypeInvalid: + break; + + case eTypeUInt8: + case eTypeUInt16: + case eTypeUInt32: + case eTypeUInt64: + case eTypeUInt128: + if (bit < (GetByteSize() * 8)) { + return m_scalar.SetBit(bit); + } + break; + + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (buffer.byte_order == eByteOrderBig || + buffer.byte_order == eByteOrderLittle) { + uint32_t byte_idx; + if (buffer.byte_order == eByteOrderBig) + byte_idx = buffer.bytes.size() - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < buffer.bytes.size()) { + buffer.bytes[byte_idx] |= (1u << byte_bit); + return true; + } + } + break; + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp b/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp new file mode 100644 index 000000000000..026793462221 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp @@ -0,0 +1,42 @@ +//===-- RegularExpression.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/Utility/RegularExpression.h" + +#include <string> + +using namespace lldb_private; + +RegularExpression::RegularExpression(llvm::StringRef str, + llvm::Regex::RegexFlags flags) + : m_regex_text(std::string(str)), + // m_regex does not reference str anymore after it is constructed. + m_regex(llvm::Regex(str, flags)) {} + +RegularExpression::RegularExpression(const RegularExpression &rhs) + : RegularExpression(rhs.GetText()) {} + +bool RegularExpression::Execute( + llvm::StringRef str, + llvm::SmallVectorImpl<llvm::StringRef> *matches) const { + if (!IsValid()) + return false; + return m_regex.match(str, matches); +} + +bool RegularExpression::IsValid() const { return m_regex.isValid(); } + +llvm::StringRef RegularExpression::GetText() const { return m_regex_text; } + +llvm::Error RegularExpression::GetError() const { + std::string error; + if (!m_regex.isValid(error)) + return llvm::make_error<llvm::StringError>(error, + llvm::inconvertibleErrorCode()); + return llvm::Error::success(); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Scalar.cpp b/contrib/llvm-project/lldb/source/Utility/Scalar.cpp new file mode 100644 index 000000000000..c680101aa9ef --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Scalar.cpp @@ -0,0 +1,939 @@ +//===-- Scalar.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/Utility/Scalar.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" + +#include <cinttypes> +#include <cstdio> + +using namespace lldb; +using namespace lldb_private; + +using llvm::APFloat; +using llvm::APInt; +using llvm::APSInt; + +Scalar::PromotionKey Scalar::GetPromoKey() const { + switch (m_type) { + case e_void: + return PromotionKey{e_void, 0, false}; + case e_int: + return PromotionKey{e_int, m_integer.getBitWidth(), m_integer.isUnsigned()}; + case e_float: + return GetFloatPromoKey(m_float.getSemantics()); + } + llvm_unreachable("Unhandled category!"); +} + +Scalar::PromotionKey Scalar::GetFloatPromoKey(const llvm::fltSemantics &sem) { + static const llvm::fltSemantics *const order[] = { + &APFloat::IEEEsingle(), &APFloat::IEEEdouble(), + &APFloat::x87DoubleExtended()}; + for (const auto &entry : llvm::enumerate(order)) { + if (entry.value() == &sem) + return PromotionKey{e_float, entry.index(), false}; + } + llvm_unreachable("Unsupported semantics!"); +} + +// Promote to max type currently follows the ANSI C rule for type promotion in +// expressions. +Scalar::Type Scalar::PromoteToMaxType(Scalar &lhs, Scalar &rhs) { + const auto &Promote = [](Scalar &a, const Scalar &b) { + switch (b.GetType()) { + case e_void: + break; + case e_int: + a.IntegralPromote(b.m_integer.getBitWidth(), b.m_integer.isSigned()); + break; + case e_float: + a.FloatPromote(b.m_float.getSemantics()); + } + }; + + PromotionKey lhs_key = lhs.GetPromoKey(); + PromotionKey rhs_key = rhs.GetPromoKey(); + + if (lhs_key > rhs_key) + Promote(rhs, lhs); + else if (rhs_key > lhs_key) + Promote(lhs, rhs); + + // Make sure our type promotion worked as expected + if (lhs.GetPromoKey() == rhs.GetPromoKey()) + return lhs.GetType(); // Return the resulting type + + // Return the void type (zero) if we fail to promote either of the values. + return Scalar::e_void; +} + +bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const { + size_t byte_size = GetByteSize(); + if (byte_size == 0) { + data.Clear(); + return false; + } + auto buffer_up = std::make_unique<DataBufferHeap>(byte_size, 0); + GetBytes(buffer_up->GetData()); + lldb::offset_t offset = 0; + + if (limit_byte_size < byte_size) { + if (endian::InlHostByteOrder() == eByteOrderLittle) { + // On little endian systems if we want fewer bytes from the current + // type we just specify fewer bytes since the LSByte is first... + byte_size = limit_byte_size; + } else if (endian::InlHostByteOrder() == eByteOrderBig) { + // On big endian systems if we want fewer bytes from the current type + // have to advance our initial byte pointer and trim down the number of + // bytes since the MSByte is first + offset = byte_size - limit_byte_size; + byte_size = limit_byte_size; + } + } + + data.SetData(std::move(buffer_up), offset, byte_size); + data.SetByteOrder(endian::InlHostByteOrder()); + return true; +} + +void Scalar::GetBytes(llvm::MutableArrayRef<uint8_t> storage) const { + assert(storage.size() >= GetByteSize()); + + const auto &store = [&](const llvm::APInt &val) { + StoreIntToMemory(val, storage.data(), (val.getBitWidth() + 7) / 8); + }; + switch (m_type) { + case e_void: + break; + case e_int: + store(m_integer); + break; + case e_float: + store(m_float.bitcastToAPInt()); + break; + } +} + +size_t Scalar::GetByteSize() const { + switch (m_type) { + case e_void: + break; + case e_int: + return (m_integer.getBitWidth() + 7) / 8; + case e_float: + return (m_float.bitcastToAPInt().getBitWidth() + 7) / 8; + } + return 0; +} + +bool Scalar::IsZero() const { + switch (m_type) { + case e_void: + break; + case e_int: + return m_integer.isZero(); + case e_float: + return m_float.isZero(); + } + return false; +} + +void Scalar::GetValue(Stream &s, bool show_type) const { + if (show_type) + s.Printf("(%s) ", GetTypeAsCString()); + + switch (m_type) { + case e_void: + break; + case e_int: + s.PutCString(llvm::toString(m_integer, 10)); + break; + case e_float: + llvm::SmallString<24> string; + m_float.toString(string); + s.PutCString(string); + break; + } +} + +void Scalar::TruncOrExtendTo(uint16_t bits, bool sign) { + m_integer.setIsSigned(sign); + m_integer = m_integer.extOrTrunc(bits); +} + +bool Scalar::IntegralPromote(uint16_t bits, bool sign) { + switch (m_type) { + case e_void: + case e_float: + break; + case e_int: + if (GetPromoKey() > PromotionKey(e_int, bits, !sign)) + break; + m_integer = m_integer.extOrTrunc(bits); + m_integer.setIsSigned(sign); + return true; + } + return false; +} + +bool Scalar::FloatPromote(const llvm::fltSemantics &semantics) { + bool success = false; + switch (m_type) { + case e_void: + break; + case e_int: + m_float = llvm::APFloat(semantics); + m_float.convertFromAPInt(m_integer, m_integer.isSigned(), + llvm::APFloat::rmNearestTiesToEven); + success = true; + break; + case e_float: + if (GetFloatPromoKey(semantics) < GetFloatPromoKey(m_float.getSemantics())) + break; + bool ignore; + success = true; + m_float.convert(semantics, llvm::APFloat::rmNearestTiesToEven, &ignore); + } + + if (success) + m_type = e_float; + return success; +} + +const char *Scalar::GetValueTypeAsCString(Scalar::Type type) { + switch (type) { + case e_void: + return "void"; + case e_int: + return "int"; + case e_float: + return "float"; + } + return "???"; +} + +bool Scalar::IsSigned() const { + switch (m_type) { + case e_void: + return false; + case e_int: + return m_integer.isSigned(); + case e_float: + return true; + } + llvm_unreachable("Unrecognized type!"); +} + +bool Scalar::MakeSigned() { + bool success = false; + + switch (m_type) { + case e_void: + break; + case e_int: + m_integer.setIsSigned(true); + success = true; + break; + case e_float: + success = true; + break; + } + + return success; +} + +bool Scalar::MakeUnsigned() { + bool success = false; + + switch (m_type) { + case e_void: + break; + case e_int: + m_integer.setIsUnsigned(true); + success = true; + break; + case e_float: + success = true; + break; + } + + return success; +} + +static llvm::APInt ToAPInt(const llvm::APFloat &f, unsigned bits, + bool is_unsigned) { + llvm::APSInt result(bits, is_unsigned); + bool isExact; + f.convertToInteger(result, llvm::APFloat::rmTowardZero, &isExact); + return std::move(result); +} + +template <typename T> T Scalar::GetAs(T fail_value) const { + switch (m_type) { + case e_void: + break; + case e_int: { + APSInt ext = m_integer.extOrTrunc(sizeof(T) * 8); + if (ext.isSigned()) + return ext.getSExtValue(); + return ext.getZExtValue(); + } + case e_float: + return ToAPInt(m_float, sizeof(T) * 8, std::is_unsigned<T>::value) + .getSExtValue(); + } + return fail_value; +} + +signed char Scalar::SChar(signed char fail_value) const { + return GetAs<signed char>(fail_value); +} + +unsigned char Scalar::UChar(unsigned char fail_value) const { + return GetAs<unsigned char>(fail_value); +} + +short Scalar::SShort(short fail_value) const { + return GetAs<short>(fail_value); +} + +unsigned short Scalar::UShort(unsigned short fail_value) const { + return GetAs<unsigned short>(fail_value); +} + +int Scalar::SInt(int fail_value) const { return GetAs<int>(fail_value); } + +unsigned int Scalar::UInt(unsigned int fail_value) const { + return GetAs<unsigned int>(fail_value); +} + +long Scalar::SLong(long fail_value) const { return GetAs<long>(fail_value); } + +unsigned long Scalar::ULong(unsigned long fail_value) const { + return GetAs<unsigned long>(fail_value); +} + +long long Scalar::SLongLong(long long fail_value) const { + return GetAs<long long>(fail_value); +} + +unsigned long long Scalar::ULongLong(unsigned long long fail_value) const { + return GetAs<unsigned long long>(fail_value); +} + +llvm::APInt Scalar::SInt128(const llvm::APInt &fail_value) const { + switch (m_type) { + case e_void: + break; + case e_int: + return m_integer; + case e_float: + return ToAPInt(m_float, 128, /*is_unsigned=*/false); + } + return fail_value; +} + +llvm::APInt Scalar::UInt128(const llvm::APInt &fail_value) const { + switch (m_type) { + case e_void: + break; + case e_int: + return m_integer; + case e_float: + return ToAPInt(m_float, 128, /*is_unsigned=*/true); + } + return fail_value; +} + +float Scalar::Float(float fail_value) const { + switch (m_type) { + case e_void: + break; + case e_int: + if (m_integer.isSigned()) + return llvm::APIntOps::RoundSignedAPIntToFloat(m_integer); + return llvm::APIntOps::RoundAPIntToFloat(m_integer); + + case e_float: { + APFloat result = m_float; + bool losesInfo; + result.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, + &losesInfo); + return result.convertToFloat(); + } + } + return fail_value; +} + +double Scalar::Double(double fail_value) const { + switch (m_type) { + case e_void: + break; + case e_int: + if (m_integer.isSigned()) + return llvm::APIntOps::RoundSignedAPIntToDouble(m_integer); + return llvm::APIntOps::RoundAPIntToDouble(m_integer); + + case e_float: { + APFloat result = m_float; + bool losesInfo; + result.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, + &losesInfo); + return result.convertToDouble(); + } + } + return fail_value; +} + +long double Scalar::LongDouble(long double fail_value) const { + /// No way to get more precision at the moment. + return static_cast<long double>(Double(fail_value)); +} + +Scalar &Scalar::operator+=(Scalar rhs) { + Scalar copy = *this; + if ((m_type = PromoteToMaxType(copy, rhs)) != Scalar::e_void) { + switch (m_type) { + case e_void: + break; + case e_int: + m_integer = copy.m_integer + rhs.m_integer; + break; + + case e_float: + m_float = copy.m_float + rhs.m_float; + break; + } + } + return *this; +} + +Scalar &Scalar::operator<<=(const Scalar &rhs) { + if (m_type == e_int && rhs.m_type == e_int) + static_cast<APInt &>(m_integer) <<= rhs.m_integer; + else + m_type = e_void; + return *this; +} + +bool Scalar::ShiftRightLogical(const Scalar &rhs) { + if (m_type == e_int && rhs.m_type == e_int) { + m_integer = m_integer.lshr(rhs.m_integer); + return true; + } + m_type = e_void; + return false; +} + +Scalar &Scalar::operator>>=(const Scalar &rhs) { + switch (m_type) { + case e_void: + case e_float: + m_type = e_void; + break; + + case e_int: + switch (rhs.m_type) { + case e_void: + case e_float: + m_type = e_void; + break; + case e_int: + m_integer = m_integer.ashr(rhs.m_integer); + break; + } + break; + } + return *this; +} + +Scalar &Scalar::operator&=(const Scalar &rhs) { + if (m_type == e_int && rhs.m_type == e_int) + m_integer &= rhs.m_integer; + else + m_type = e_void; + return *this; +} + +bool Scalar::AbsoluteValue() { + switch (m_type) { + case e_void: + break; + + case e_int: + if (m_integer.isNegative()) + m_integer = -m_integer; + return true; + + case e_float: + m_float.clearSign(); + return true; + } + return false; +} + +bool Scalar::UnaryNegate() { + switch (m_type) { + case e_void: + break; + case e_int: + m_integer = -m_integer; + return true; + case e_float: + m_float.changeSign(); + return true; + } + return false; +} + +bool Scalar::OnesComplement() { + if (m_type == e_int) { + m_integer = ~m_integer; + return true; + } + + return false; +} + +const Scalar lldb_private::operator+(const Scalar &lhs, const Scalar &rhs) { + Scalar result = lhs; + result += rhs; + return result; +} + +const Scalar lldb_private::operator-(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + switch (result.m_type) { + case Scalar::e_void: + break; + case Scalar::e_int: + result.m_integer = lhs.m_integer - rhs.m_integer; + break; + case Scalar::e_float: + result.m_float = lhs.m_float - rhs.m_float; + break; + } + } + return result; +} + +const Scalar lldb_private::operator/(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void && + !rhs.IsZero()) { + switch (result.m_type) { + case Scalar::e_void: + break; + case Scalar::e_int: + result.m_integer = lhs.m_integer / rhs.m_integer; + return result; + case Scalar::e_float: + result.m_float = lhs.m_float / rhs.m_float; + return result; + } + } + // For division only, the only way it should make it here is if a promotion + // failed, or if we are trying to do a divide by zero. + result.m_type = Scalar::e_void; + return result; +} + +const Scalar lldb_private::operator*(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + switch (result.m_type) { + case Scalar::e_void: + break; + case Scalar::e_int: + result.m_integer = lhs.m_integer * rhs.m_integer; + break; + case Scalar::e_float: + result.m_float = lhs.m_float * rhs.m_float; + break; + } + } + return result; +} + +const Scalar lldb_private::operator&(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + if (result.m_type == Scalar::e_int) + result.m_integer = lhs.m_integer & rhs.m_integer; + else + result.m_type = Scalar::e_void; + } + return result; +} + +const Scalar lldb_private::operator|(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + if (result.m_type == Scalar::e_int) + result.m_integer = lhs.m_integer | rhs.m_integer; + else + result.m_type = Scalar::e_void; + } + return result; +} + +const Scalar lldb_private::operator%(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + if (!rhs.IsZero() && result.m_type == Scalar::e_int) { + result.m_integer = lhs.m_integer % rhs.m_integer; + return result; + } + } + result.m_type = Scalar::e_void; + return result; +} + +const Scalar lldb_private::operator^(Scalar lhs, Scalar rhs) { + Scalar result; + if ((result.m_type = Scalar::PromoteToMaxType(lhs, rhs)) != Scalar::e_void) { + if (result.m_type == Scalar::e_int) + result.m_integer = lhs.m_integer ^ rhs.m_integer; + else + result.m_type = Scalar::e_void; + } + return result; +} + +const Scalar lldb_private::operator<<(const Scalar &lhs, const Scalar &rhs) { + Scalar result = lhs; + result <<= rhs; + return result; +} + +const Scalar lldb_private::operator>>(const Scalar &lhs, const Scalar &rhs) { + Scalar result = lhs; + result >>= rhs; + return result; +} + +Status Scalar::SetValueFromCString(const char *value_str, Encoding encoding, + size_t byte_size) { + Status error; + if (value_str == nullptr || value_str[0] == '\0') { + error.SetErrorString("Invalid c-string value string."); + return error; + } + switch (encoding) { + case eEncodingInvalid: + error.SetErrorString("Invalid encoding."); + break; + + case eEncodingSint: + case eEncodingUint: { + llvm::StringRef str = value_str; + bool is_signed = encoding == eEncodingSint; + bool is_negative = is_signed && str.consume_front("-"); + APInt integer; + if (str.getAsInteger(0, integer)) { + error.SetErrorStringWithFormatv( + "'{0}' is not a valid integer string value", value_str); + break; + } + bool fits; + if (is_signed) { + integer = integer.zext(integer.getBitWidth() + 1); + if (is_negative) + integer.negate(); + fits = integer.isSignedIntN(byte_size * 8); + } else + fits = integer.isIntN(byte_size * 8); + if (!fits) { + error.SetErrorStringWithFormatv( + "value {0} is too large to fit in a {1} byte integer value", + value_str, byte_size); + break; + } + m_type = e_int; + m_integer = + APSInt(std::move(integer), !is_signed).extOrTrunc(8 * byte_size); + break; + } + + case eEncodingIEEE754: { + // FIXME: It's not possible to unambiguously map a byte size to a floating + // point type. This function should be refactored to take an explicit + // semantics argument. + const llvm::fltSemantics &sem = + byte_size <= 4 ? APFloat::IEEEsingle() + : byte_size <= 8 ? APFloat::IEEEdouble() + : APFloat::x87DoubleExtended(); + APFloat f(sem); + if (llvm::Expected<APFloat::opStatus> op = + f.convertFromString(value_str, APFloat::rmNearestTiesToEven)) { + m_type = e_float; + m_float = std::move(f); + } else + error = op.takeError(); + break; + } + + case eEncodingVector: + error.SetErrorString("vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +Status Scalar::SetValueFromData(const DataExtractor &data, + lldb::Encoding encoding, size_t byte_size) { + Status error; + switch (encoding) { + case lldb::eEncodingInvalid: + error.SetErrorString("invalid encoding"); + break; + case lldb::eEncodingVector: + error.SetErrorString("vector encoding unsupported"); + break; + case lldb::eEncodingUint: + case lldb::eEncodingSint: { + if (data.GetByteSize() < byte_size) + return Status("insufficient data"); + m_type = e_int; + m_integer = + APSInt(APInt::getZero(8 * byte_size), encoding == eEncodingUint); + if (data.GetByteOrder() == endian::InlHostByteOrder()) { + llvm::LoadIntFromMemory(m_integer, data.GetDataStart(), byte_size); + } else { + std::vector<uint8_t> buffer(byte_size); + std::copy_n(data.GetDataStart(), byte_size, buffer.rbegin()); + llvm::LoadIntFromMemory(m_integer, buffer.data(), byte_size); + } + break; + } + case lldb::eEncodingIEEE754: { + lldb::offset_t offset = 0; + + if (byte_size == sizeof(float)) + operator=(data.GetFloat(&offset)); + else if (byte_size == sizeof(double)) + operator=(data.GetDouble(&offset)); + else if (byte_size == sizeof(long double)) + operator=(data.GetLongDouble(&offset)); + else + error.SetErrorStringWithFormat("unsupported float byte size: %" PRIu64 "", + static_cast<uint64_t>(byte_size)); + } break; + } + + return error; +} + +bool Scalar::SignExtend(uint32_t sign_bit_pos) { + const uint32_t max_bit_pos = GetByteSize() * 8; + + if (sign_bit_pos < max_bit_pos) { + switch (m_type) { + case Scalar::e_void: + case Scalar::e_float: + return false; + + case Scalar::e_int: + if (sign_bit_pos < (max_bit_pos - 1)) { + llvm::APInt sign_bit = llvm::APInt::getSignMask(sign_bit_pos + 1); + llvm::APInt bitwize_and = m_integer & sign_bit; + if (bitwize_and.getBoolValue()) { + llvm::APInt mask = + ~(sign_bit) + llvm::APInt(m_integer.getBitWidth(), 1); + m_integer |= APSInt(std::move(mask), m_integer.isUnsigned()); + } + return true; + } + break; + } + } + return false; +} + +size_t Scalar::GetAsMemoryData(void *dst, size_t dst_len, + lldb::ByteOrder dst_byte_order, + Status &error) const { + // Get a data extractor that points to the native scalar data + DataExtractor data; + if (!GetData(data)) { + error.SetErrorString("invalid scalar value"); + return 0; + } + + const size_t src_len = data.GetByteSize(); + + // Prepare a memory buffer that contains some or all of the register value + const size_t bytes_copied = + data.CopyByteOrderedData(0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorString("failed to copy data"); + + return bytes_copied; +} + +bool Scalar::ExtractBitfield(uint32_t bit_size, uint32_t bit_offset) { + if (bit_size == 0) + return true; + + switch (m_type) { + case Scalar::e_void: + case Scalar::e_float: + break; + + case Scalar::e_int: + m_integer >>= bit_offset; + m_integer = m_integer.extOrTrunc(bit_size).extOrTrunc(8 * GetByteSize()); + return true; + } + return false; +} + +llvm::APFloat Scalar::CreateAPFloatFromAPSInt(lldb::BasicType basic_type) { + switch (basic_type) { + case lldb::eBasicTypeFloat: + return llvm::APFloat( + m_integer.isSigned() + ? llvm::APIntOps::RoundSignedAPIntToFloat(m_integer) + : llvm::APIntOps::RoundAPIntToFloat(m_integer)); + case lldb::eBasicTypeDouble: + // No way to get more precision at the moment. + case lldb::eBasicTypeLongDouble: + return llvm::APFloat( + m_integer.isSigned() + ? llvm::APIntOps::RoundSignedAPIntToDouble(m_integer) + : llvm::APIntOps::RoundAPIntToDouble(m_integer)); + default: + const llvm::fltSemantics &sem = APFloat::IEEEsingle(); + return llvm::APFloat::getNaN(sem); + } +} + +llvm::APFloat Scalar::CreateAPFloatFromAPFloat(lldb::BasicType basic_type) { + switch (basic_type) { + case lldb::eBasicTypeFloat: { + bool loses_info; + m_float.convert(llvm::APFloat::IEEEsingle(), + llvm::APFloat::rmNearestTiesToEven, &loses_info); + return m_float; + } + case lldb::eBasicTypeDouble: + // No way to get more precision at the moment. + case lldb::eBasicTypeLongDouble: { + bool loses_info; + m_float.convert(llvm::APFloat::IEEEdouble(), + llvm::APFloat::rmNearestTiesToEven, &loses_info); + return m_float; + } + default: + const llvm::fltSemantics &sem = APFloat::IEEEsingle(); + return llvm::APFloat::getNaN(sem); + } +} + +bool lldb_private::operator==(Scalar lhs, Scalar rhs) { + // If either entry is void then we can just compare the types + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return lhs.m_type == rhs.m_type; + + llvm::APFloat::cmpResult result; + switch (Scalar::PromoteToMaxType(lhs, rhs)) { + case Scalar::e_void: + break; + case Scalar::e_int: + return lhs.m_integer == rhs.m_integer; + case Scalar::e_float: + result = lhs.m_float.compare(rhs.m_float); + if (result == llvm::APFloat::cmpEqual) + return true; + } + return false; +} + +bool lldb_private::operator!=(const Scalar &lhs, const Scalar &rhs) { + return !(lhs == rhs); +} + +bool lldb_private::operator<(Scalar lhs, Scalar rhs) { + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + llvm::APFloat::cmpResult result; + switch (Scalar::PromoteToMaxType(lhs, rhs)) { + case Scalar::e_void: + break; + case Scalar::e_int: + return lhs.m_integer < rhs.m_integer; + case Scalar::e_float: + result = lhs.m_float.compare(rhs.m_float); + if (result == llvm::APFloat::cmpLessThan) + return true; + } + return false; +} + +bool lldb_private::operator<=(const Scalar &lhs, const Scalar &rhs) { + return !(rhs < lhs); +} + +bool lldb_private::operator>(const Scalar &lhs, const Scalar &rhs) { + return rhs < lhs; +} + +bool lldb_private::operator>=(const Scalar &lhs, const Scalar &rhs) { + return !(lhs < rhs); +} + +bool Scalar::ClearBit(uint32_t bit) { + switch (m_type) { + case e_void: + break; + case e_int: + m_integer.clearBit(bit); + return true; + case e_float: + break; + } + return false; +} + +bool Scalar::SetBit(uint32_t bit) { + switch (m_type) { + case e_void: + break; + case e_int: + m_integer.setBit(bit); + return true; + case e_float: + break; + } + return false; +} + +llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &os, const Scalar &scalar) { + StreamString s; + scalar.GetValue(s, /*show_type*/ true); + return os << s.GetString(); +} diff --git a/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp b/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp new file mode 100644 index 000000000000..b7d9e19bc7cb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp @@ -0,0 +1,254 @@ +//===-- SelectHelper.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +#include "llvm/ADT/DenseMap.h" + +#include <algorithm> +#include <chrono> +#include <optional> + +#include <cerrno> +#if defined(_WIN32) +// Define NOMINMAX to avoid macros that conflict with std::min and std::max +#define NOMINMAX +#include <winsock2.h> +#else +#include <sys/time.h> +#include <sys/select.h> +#endif + + +SelectHelper::SelectHelper() + : m_fd_map(), m_end_time() // Infinite timeout unless + // SelectHelper::SetTimeout() gets called +{} + +void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) { + using namespace std::chrono; + m_end_time = steady_clock::time_point(steady_clock::now() + timeout); +} + +void SelectHelper::FDSetRead(lldb::socket_t fd) { + m_fd_map[fd].read_set = true; +} + +void SelectHelper::FDSetWrite(lldb::socket_t fd) { + m_fd_map[fd].write_set = true; +} + +void SelectHelper::FDSetError(lldb::socket_t fd) { + m_fd_map[fd].error_set = true; +} + +bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const { + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.read_is_set; + else + return false; +} + +bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const { + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.write_is_set; + else + return false; +} + +bool SelectHelper::FDIsSetError(lldb::socket_t fd) const { + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.error_is_set; + else + return false; +} + +static void updateMaxFd(std::optional<lldb::socket_t> &vold, + lldb::socket_t vnew) { + if (!vold) + vold = vnew; + else + vold = std::max(*vold, vnew); +} + +lldb_private::Status SelectHelper::Select() { + lldb_private::Status error; +#ifdef _WIN32 + // On windows FD_SETSIZE limits the number of file descriptors, not their + // numeric value. + lldbassert(m_fd_map.size() <= FD_SETSIZE); + if (m_fd_map.size() > FD_SETSIZE) + return lldb_private::Status("Too many file descriptors for select()"); +#endif + + std::optional<lldb::socket_t> max_read_fd; + std::optional<lldb::socket_t> max_write_fd; + std::optional<lldb::socket_t> max_error_fd; + std::optional<lldb::socket_t> max_fd; + for (auto &pair : m_fd_map) { + pair.second.PrepareForSelect(); + const lldb::socket_t fd = pair.first; +#if !defined(__APPLE__) && !defined(_WIN32) + lldbassert(fd < static_cast<int>(FD_SETSIZE)); + if (fd >= static_cast<int>(FD_SETSIZE)) { + error.SetErrorStringWithFormat("%i is too large for select()", fd); + return error; + } +#endif + if (pair.second.read_set) + updateMaxFd(max_read_fd, fd); + if (pair.second.write_set) + updateMaxFd(max_write_fd, fd); + if (pair.second.error_set) + updateMaxFd(max_error_fd, fd); + updateMaxFd(max_fd, fd); + } + + if (!max_fd) { + error.SetErrorString("no valid file descriptors"); + return error; + } + + const unsigned nfds = static_cast<unsigned>(*max_fd) + 1; + fd_set *read_fdset_ptr = nullptr; + fd_set *write_fdset_ptr = nullptr; + fd_set *error_fdset_ptr = nullptr; +// Initialize and zero out the fdsets +#if defined(__APPLE__) + llvm::SmallVector<fd_set, 1> read_fdset; + llvm::SmallVector<fd_set, 1> write_fdset; + llvm::SmallVector<fd_set, 1> error_fdset; + + if (max_read_fd.has_value()) { + read_fdset.resize((nfds / FD_SETSIZE) + 1); + read_fdset_ptr = read_fdset.data(); + } + if (max_write_fd.has_value()) { + write_fdset.resize((nfds / FD_SETSIZE) + 1); + write_fdset_ptr = write_fdset.data(); + } + if (max_error_fd.has_value()) { + error_fdset.resize((nfds / FD_SETSIZE) + 1); + error_fdset_ptr = error_fdset.data(); + } + for (auto &fd_set : read_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : write_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : error_fdset) + FD_ZERO(&fd_set); +#else + fd_set read_fdset; + fd_set write_fdset; + fd_set error_fdset; + + if (max_read_fd) { + FD_ZERO(&read_fdset); + read_fdset_ptr = &read_fdset; + } + if (max_write_fd) { + FD_ZERO(&write_fdset); + write_fdset_ptr = &write_fdset; + } + if (max_error_fd) { + FD_ZERO(&error_fdset); + error_fdset_ptr = &error_fdset; + } +#endif + // Set the FD bits in the fdsets for read/write/error + for (auto &pair : m_fd_map) { + const lldb::socket_t fd = pair.first; + + if (pair.second.read_set) + FD_SET(fd, read_fdset_ptr); + + if (pair.second.write_set) + FD_SET(fd, write_fdset_ptr); + + if (pair.second.error_set) + FD_SET(fd, error_fdset_ptr); + } + + // Setup our timeout time value if needed + struct timeval *tv_ptr = nullptr; + struct timeval tv = {0, 0}; + + while (true) { + using namespace std::chrono; + // Setup out relative timeout based on the end time if we have one + if (m_end_time) { + tv_ptr = &tv; + const auto remaining_dur = + duration_cast<microseconds>(*m_end_time - steady_clock::now()); + if (remaining_dur.count() > 0) { + // Wait for a specific amount of time + const auto dur_secs = duration_cast<seconds>(remaining_dur); + const auto dur_usecs = remaining_dur % seconds(1); + tv.tv_sec = dur_secs.count(); + tv.tv_usec = dur_usecs.count(); + } else { + // Just poll once with no timeout + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, + error_fdset_ptr, tv_ptr); + if (num_set_fds < 0) { + // We got an error + error.SetErrorToErrno(); + if (error.GetError() == EINTR) { + error.Clear(); + continue; // Keep calling select if we get EINTR + } else + return error; + } else if (num_set_fds == 0) { + // Timeout + error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX); + error.SetErrorString("timed out"); + return error; + } else { + // One or more descriptors were set, update the FDInfo::select_is_set + // mask so users can ask the SelectHelper class so clients can call one + // of: + + for (auto &pair : m_fd_map) { + const int fd = pair.first; + + if (pair.second.read_set) { + if (FD_ISSET(fd, read_fdset_ptr)) + pair.second.read_is_set = true; + } + if (pair.second.write_set) { + if (FD_ISSET(fd, write_fdset_ptr)) + pair.second.write_is_set = true; + } + if (pair.second.error_set) { + if (FD_ISSET(fd, error_fdset_ptr)) + pair.second.error_is_set = true; + } + } + break; + } + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Utility/State.cpp b/contrib/llvm-project/lldb/source/Utility/State.cpp new file mode 100644 index 000000000000..2d76b801dcaa --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/State.cpp @@ -0,0 +1,110 @@ +//===-- State.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/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +const char *lldb_private::StateAsCString(StateType state) { + switch (state) { + case eStateInvalid: + return "invalid"; + case eStateUnloaded: + return "unloaded"; + case eStateConnected: + return "connected"; + case eStateAttaching: + return "attaching"; + case eStateLaunching: + return "launching"; + case eStateStopped: + return "stopped"; + case eStateRunning: + return "running"; + case eStateStepping: + return "stepping"; + case eStateCrashed: + return "crashed"; + case eStateDetached: + return "detached"; + case eStateExited: + return "exited"; + case eStateSuspended: + return "suspended"; + } + return "unknown"; +} + +const char *lldb_private::GetPermissionsAsCString(uint32_t permissions) { + switch (permissions) { + case 0: + return "---"; + case ePermissionsWritable: + return "-w-"; + case ePermissionsReadable: + return "r--"; + case ePermissionsExecutable: + return "--x"; + case ePermissionsReadable | ePermissionsWritable: + return "rw-"; + case ePermissionsReadable | ePermissionsExecutable: + return "r-x"; + case ePermissionsWritable | ePermissionsExecutable: + return "-wx"; + case ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable: + return "rwx"; + default: + break; + } + return "???"; +} + +bool lldb_private::StateIsRunningState(StateType state) { + switch (state) { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return true; + + case eStateConnected: + case eStateDetached: + case eStateInvalid: + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + break; + } + return false; +} + +bool lldb_private::StateIsStoppedState(StateType state, bool must_exist) { + switch (state) { + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + break; + + case eStateUnloaded: + case eStateExited: + return !must_exist; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Utility/Status.cpp b/contrib/llvm-project/lldb/source/Utility/Status.cpp new file mode 100644 index 000000000000..18312e87f03e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Status.cpp @@ -0,0 +1,285 @@ +//===-- Status.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/Utility/Status.h" + +#include "lldb/Utility/VASPrintf.h" +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FormatProviders.h" + +#include <cerrno> +#include <cstdarg> +#include <string> +#include <system_error> + +#ifdef __APPLE__ +#include <mach/mach.h> +#endif + +#ifdef _WIN32 +#include <windows.h> +#endif +#include <cstdint> + +namespace llvm { +class raw_ostream; +} + +using namespace lldb; +using namespace lldb_private; + +Status::Status() : m_string() {} + +Status::Status(ValueType err, ErrorType type) + : m_code(err), m_type(type), m_string() {} + +// This logic is confusing because c++ calls the traditional (posix) errno codes +// "generic errors", while we use the term "generic" to mean completely +// arbitrary (text-based) errors. +Status::Status(std::error_code EC) + : m_code(EC.value()), + m_type(EC.category() == std::generic_category() ? eErrorTypePOSIX + : eErrorTypeGeneric), + m_string(EC.message()) {} + +Status::Status(const char *format, ...) : m_string() { + va_list args; + va_start(args, format); + SetErrorToGenericError(); + SetErrorStringWithVarArg(format, args); + va_end(args); +} + +const Status &Status::operator=(llvm::Error error) { + if (!error) { + Clear(); + return *this; + } + + // if the error happens to be a errno error, preserve the error code + error = llvm::handleErrors( + std::move(error), [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error { + std::error_code ec = e->convertToErrorCode(); + if (ec.category() == std::generic_category()) { + m_code = ec.value(); + m_type = ErrorType::eErrorTypePOSIX; + return llvm::Error::success(); + } + return llvm::Error(std::move(e)); + }); + + // Otherwise, just preserve the message + if (error) { + SetErrorToGenericError(); + SetErrorString(llvm::toString(std::move(error))); + } + + return *this; +} + +llvm::Error Status::ToError() const { + if (Success()) + return llvm::Error::success(); + if (m_type == ErrorType::eErrorTypePOSIX) + return llvm::errorCodeToError( + std::error_code(m_code, std::generic_category())); + return llvm::createStringError(AsCString()); +} + +Status::~Status() = default; + +#ifdef _WIN32 +static std::string RetrieveWin32ErrorString(uint32_t error_code) { + char *buffer = nullptr; + std::string message; + // Retrieve win32 system error. + // First, attempt to load a en-US message + if (::FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&buffer, 0, NULL)) { + message.assign(buffer); + ::LocalFree(buffer); + } + // If the previous didn't work, use the default OS language + else if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, error_code, 0, (LPSTR)&buffer, 0, NULL)) { + message.assign(buffer); + ::LocalFree(buffer); + } + return message; +} +#endif + +// Get the error value as a NULL C string. The error string will be fetched and +// cached on demand. The cached error string value will remain until the error +// value is changed or cleared. +const char *Status::AsCString(const char *default_error_str) const { + if (Success()) + return nullptr; + + if (m_string.empty()) { + switch (m_type) { + case eErrorTypeMachKernel: +#if defined(__APPLE__) + if (const char *s = ::mach_error_string(m_code)) + m_string.assign(s); +#endif + break; + + case eErrorTypePOSIX: + m_string = llvm::sys::StrError(m_code); + break; + + case eErrorTypeWin32: +#if defined(_WIN32) + m_string = RetrieveWin32ErrorString(m_code); +#endif + break; + + default: + break; + } + } + if (m_string.empty()) { + if (default_error_str) + m_string.assign(default_error_str); + else + return nullptr; // User wanted a nullptr string back... + } + return m_string.c_str(); +} + +// Clear the error and any cached error string that it might contain. +void Status::Clear() { + m_code = 0; + m_type = eErrorTypeInvalid; + m_string.clear(); +} + +// Access the error value. +Status::ValueType Status::GetError() const { return m_code; } + +// Access the error type. +ErrorType Status::GetType() const { return m_type; } + +// Returns true if this object contains a value that describes an error or +// otherwise non-success result. +bool Status::Fail() const { return m_code != 0; } + +void Status::SetExpressionError(lldb::ExpressionResults result, + const char *mssg) { + m_code = result; + m_type = eErrorTypeExpression; + m_string = mssg; +} + +int Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result, + const char *format, ...) { + int length = 0; + + if (format != nullptr && format[0]) { + va_list args; + va_start(args, format); + length = SetErrorStringWithVarArg(format, args); + va_end(args); + } else { + m_string.clear(); + } + m_code = result; + m_type = eErrorTypeExpression; + return length; +} + +// Set accessor for the error value and type. +void Status::SetError(ValueType err, ErrorType type) { + m_code = err; + m_type = type; + m_string.clear(); +} + +// Update the error value to be "errno" and update the type to be "POSIX". +void Status::SetErrorToErrno() { + m_code = errno; + m_type = eErrorTypePOSIX; + m_string.clear(); +} + +// Update the error value to be LLDB_GENERIC_ERROR and update the type to be +// "Generic". +void Status::SetErrorToGenericError() { + m_code = LLDB_GENERIC_ERROR; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +// Set accessor for the error string value for a specific error. This allows +// any string to be supplied as an error explanation. The error string value +// will remain until the error value is cleared or a new error value/type is +// assigned. +void Status::SetErrorString(llvm::StringRef err_str) { + if (!err_str.empty()) { + // If we have an error string, we should always at least have an error set + // to a generic value. + if (Success()) + SetErrorToGenericError(); + } + m_string = std::string(err_str); +} + +/// Set the current error string to a formatted error string. +/// +/// \param format +/// A printf style format string +int Status::SetErrorStringWithFormat(const char *format, ...) { + if (format != nullptr && format[0]) { + va_list args; + va_start(args, format); + int length = SetErrorStringWithVarArg(format, args); + va_end(args); + return length; + } else { + m_string.clear(); + } + return 0; +} + +int Status::SetErrorStringWithVarArg(const char *format, va_list args) { + if (format != nullptr && format[0]) { + // If we have an error string, we should always at least have an error set + // to a generic value. + if (Success()) + SetErrorToGenericError(); + + llvm::SmallString<1024> buf; + VASprintf(buf, format, args); + m_string = std::string(buf.str()); + return buf.size(); + } else { + m_string.clear(); + } + return 0; +} + +// Returns true if the error code in this object is considered a successful +// return value. +bool Status::Success() const { return m_code == 0; } + +void llvm::format_provider<lldb_private::Status>::format( + const lldb_private::Status &error, llvm::raw_ostream &OS, + llvm::StringRef Options) { + llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS, + Options); +} diff --git a/contrib/llvm-project/lldb/source/Utility/Stream.cpp b/contrib/llvm-project/lldb/source/Utility/Stream.cpp new file mode 100644 index 000000000000..89dce9fb0e1f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Stream.cpp @@ -0,0 +1,419 @@ +//===-- Stream.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/Utility/Stream.h" + +#include "lldb/Utility/AnsiTerminal.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/VASPrintf.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/Regex.h" + +#include <string> + +#include <cinttypes> +#include <cstddef> + +using namespace lldb; +using namespace lldb_private; + +Stream::Stream(uint32_t flags, uint32_t addr_size, ByteOrder byte_order, + bool colors) + : m_flags(flags), m_addr_size(addr_size), m_byte_order(byte_order), + m_forwarder(*this, colors) {} + +Stream::Stream(bool colors) + : m_flags(0), m_byte_order(endian::InlHostByteOrder()), + m_forwarder(*this, colors) {} + +// Destructor +Stream::~Stream() = default; + +ByteOrder Stream::SetByteOrder(ByteOrder byte_order) { + ByteOrder old_byte_order = m_byte_order; + m_byte_order = byte_order; + return old_byte_order; +} + +// Put an offset "uval" out to the stream using the printf format in "format". +void Stream::Offset(uint32_t uval, const char *format) { Printf(format, uval); } + +// Put an SLEB128 "uval" out to the stream using the printf format in "format". +size_t Stream::PutSLEB128(int64_t sval) { + if (m_flags.Test(eBinary)) + return llvm::encodeSLEB128(sval, m_forwarder); + else + return Printf("0x%" PRIi64, sval); +} + +// Put an ULEB128 "uval" out to the stream using the printf format in "format". +size_t Stream::PutULEB128(uint64_t uval) { + if (m_flags.Test(eBinary)) + return llvm::encodeULEB128(uval, m_forwarder); + else + return Printf("0x%" PRIx64, uval); +} + +// Print a raw NULL terminated C string to the stream. +size_t Stream::PutCString(llvm::StringRef str) { + size_t bytes_written = 0; + bytes_written = Write(str.data(), str.size()); + + // when in binary mode, emit the NULL terminator + if (m_flags.Test(eBinary)) + bytes_written += PutChar('\0'); + return bytes_written; +} + +void Stream::PutCStringColorHighlighted( + llvm::StringRef text, std::optional<HighlightSettings> pattern_info) { + // Only apply color formatting when a pattern information is specified. + // Otherwise, output the text without color formatting. + if (!pattern_info.has_value()) { + PutCString(text); + return; + } + + llvm::Regex reg_pattern(pattern_info->pattern); + llvm::SmallVector<llvm::StringRef, 1> matches; + llvm::StringRef remaining = text; + std::string format_str = lldb_private::ansi::FormatAnsiTerminalCodes( + pattern_info->prefix.str() + "%.*s" + pattern_info->suffix.str()); + while (reg_pattern.match(remaining, &matches)) { + llvm::StringRef match = matches[0]; + size_t match_start_pos = match.data() - remaining.data(); + PutCString(remaining.take_front(match_start_pos)); + Printf(format_str.c_str(), match.size(), match.data()); + remaining = remaining.drop_front(match_start_pos + match.size()); + } + if (remaining.size()) + PutCString(remaining); +} + +// Print a double quoted NULL terminated C string to the stream using the +// printf format in "format". +void Stream::QuotedCString(const char *cstr, const char *format) { + Printf(format, cstr); +} + +// Put an address "addr" out to the stream with optional prefix and suffix +// strings. +void lldb_private::DumpAddress(llvm::raw_ostream &s, uint64_t addr, + uint32_t addr_size, const char *prefix, + const char *suffix) { + if (prefix == nullptr) + prefix = ""; + if (suffix == nullptr) + suffix = ""; + s << prefix << llvm::format_hex(addr, 2 + 2 * addr_size) << suffix; +} + +// Put an address range out to the stream with optional prefix and suffix +// strings. +void lldb_private::DumpAddressRange(llvm::raw_ostream &s, uint64_t lo_addr, + uint64_t hi_addr, uint32_t addr_size, + const char *prefix, const char *suffix) { + if (prefix && prefix[0]) + s << prefix; + DumpAddress(s, lo_addr, addr_size, "["); + DumpAddress(s, hi_addr, addr_size, "-", ")"); + if (suffix && suffix[0]) + s << suffix; +} + +size_t Stream::PutChar(char ch) { return Write(&ch, 1); } + +// Print some formatted output to the stream. +size_t Stream::Printf(const char *format, ...) { + va_list args; + va_start(args, format); + size_t result = PrintfVarArg(format, args); + va_end(args); + return result; +} + +// Print some formatted output to the stream. +size_t Stream::PrintfVarArg(const char *format, va_list args) { + llvm::SmallString<1024> buf; + VASprintf(buf, format, args); + + // Include the NULL termination byte for binary output + size_t length = buf.size(); + if (m_flags.Test(eBinary)) + ++length; + return Write(buf.c_str(), length); +} + +// Print and End of Line character to the stream +size_t Stream::EOL() { return PutChar('\n'); } + +size_t Stream::Indent(llvm::StringRef str) { + const size_t ind_length = PutCString(std::string(m_indent_level, ' ')); + const size_t str_length = PutCString(str); + return ind_length + str_length; +} + +// Stream a character "ch" out to this stream. +Stream &Stream::operator<<(char ch) { + PutChar(ch); + return *this; +} + +// Stream the NULL terminated C string out to this stream. +Stream &Stream::operator<<(const char *s) { + Printf("%s", s); + return *this; +} + +Stream &Stream::operator<<(llvm::StringRef str) { + Write(str.data(), str.size()); + return *this; +} + +// Stream the pointer value out to this stream. +Stream &Stream::operator<<(const void *p) { + Printf("0x%.*tx", static_cast<int>(sizeof(const void *)) * 2, (ptrdiff_t)p); + return *this; +} + +// Get the current indentation level +unsigned Stream::GetIndentLevel() const { return m_indent_level; } + +// Set the current indentation level +void Stream::SetIndentLevel(unsigned indent_level) { + m_indent_level = indent_level; +} + +// Increment the current indentation level +void Stream::IndentMore(unsigned amount) { m_indent_level += amount; } + +// Decrement the current indentation level +void Stream::IndentLess(unsigned amount) { + if (m_indent_level >= amount) + m_indent_level -= amount; + else + m_indent_level = 0; +} + +// Get the address size in bytes +uint32_t Stream::GetAddressByteSize() const { return m_addr_size; } + +// Set the address size in bytes +void Stream::SetAddressByteSize(uint32_t addr_size) { m_addr_size = addr_size; } + +// The flags get accessor +Flags &Stream::GetFlags() { return m_flags; } + +// The flags const get accessor +const Flags &Stream::GetFlags() const { return m_flags; } + +// The byte order get accessor + +lldb::ByteOrder Stream::GetByteOrder() const { return m_byte_order; } + +size_t Stream::PrintfAsRawHex8(const char *format, ...) { + va_list args; + va_start(args, format); + + llvm::SmallString<1024> buf; + VASprintf(buf, format, args); + + ByteDelta delta(*this); + for (char C : buf) + _PutHex8(C, false); + + va_end(args); + + return *delta; +} + +size_t Stream::PutNHex8(size_t n, uint8_t uvalue) { + ByteDelta delta(*this); + for (size_t i = 0; i < n; ++i) + _PutHex8(uvalue, false); + return *delta; +} + +void Stream::_PutHex8(uint8_t uvalue, bool add_prefix) { + if (m_flags.Test(eBinary)) { + Write(&uvalue, 1); + } else { + if (add_prefix) + PutCString("0x"); + + static char g_hex_to_ascii_hex_char[16] = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f'}; + char nibble_chars[2]; + nibble_chars[0] = g_hex_to_ascii_hex_char[(uvalue >> 4) & 0xf]; + nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf]; + Write(nibble_chars, sizeof(nibble_chars)); + } +} + +size_t Stream::PutHex8(uint8_t uvalue) { + ByteDelta delta(*this); + _PutHex8(uvalue, false); + return *delta; +} + +size_t Stream::PutHex16(uint16_t uvalue, ByteOrder byte_order) { + ByteDelta delta(*this); + + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } + return *delta; +} + +size_t Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) { + ByteDelta delta(*this); + + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } + return *delta; +} + +size_t Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) { + ByteDelta delta(*this); + + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false); + } + return *delta; +} + +size_t Stream::PutMaxHex64(uint64_t uvalue, size_t byte_size, + lldb::ByteOrder byte_order) { + switch (byte_size) { + case 1: + return PutHex8(static_cast<uint8_t>(uvalue)); + case 2: + return PutHex16(static_cast<uint16_t>(uvalue), byte_order); + case 4: + return PutHex32(static_cast<uint32_t>(uvalue), byte_order); + case 8: + return PutHex64(uvalue, byte_order); + } + return 0; +} + +size_t Stream::PutPointer(void *ptr) { + return PutRawBytes(&ptr, sizeof(ptr), endian::InlHostByteOrder(), + endian::InlHostByteOrder()); +} + +size_t Stream::PutFloat(float f, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes(&f, sizeof(f), endian::InlHostByteOrder(), byte_order); +} + +size_t Stream::PutDouble(double d, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes(&d, sizeof(d), endian::InlHostByteOrder(), byte_order); +} + +size_t Stream::PutLongDouble(long double ld, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes(&ld, sizeof(ld), endian::InlHostByteOrder(), byte_order); +} + +size_t Stream::PutRawBytes(const void *s, size_t src_len, + ByteOrder src_byte_order, ByteOrder dst_byte_order) { + ByteDelta delta(*this); + + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + const uint8_t *src = static_cast<const uint8_t *>(s); + bool binary_was_set = m_flags.Test(eBinary); + if (!binary_was_set) + m_flags.Set(eBinary); + if (src_byte_order == dst_byte_order) { + for (size_t i = 0; i < src_len; ++i) + _PutHex8(src[i], false); + } else { + for (size_t i = src_len; i > 0; --i) + _PutHex8(src[i - 1], false); + } + if (!binary_was_set) + m_flags.Clear(eBinary); + + return *delta; +} + +size_t Stream::PutBytesAsRawHex8(const void *s, size_t src_len, + ByteOrder src_byte_order, + ByteOrder dst_byte_order) { + ByteDelta delta(*this); + + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + const uint8_t *src = static_cast<const uint8_t *>(s); + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + if (src_byte_order == dst_byte_order) { + for (size_t i = 0; i < src_len; ++i) + _PutHex8(src[i], false); + } else { + for (size_t i = src_len; i > 0; --i) + _PutHex8(src[i - 1], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return *delta; +} + +size_t Stream::PutStringAsRawHex8(llvm::StringRef s) { + ByteDelta delta(*this); + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + for (char c : s) + _PutHex8(c, false); + if (binary_is_set) + m_flags.Set(eBinary); + return *delta; +} diff --git a/contrib/llvm-project/lldb/source/Utility/StreamString.cpp b/contrib/llvm-project/lldb/source/Utility/StreamString.cpp new file mode 100644 index 000000000000..0d35ccbdbbd0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StreamString.cpp @@ -0,0 +1,66 @@ +//===-- StreamString.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/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +StreamString::StreamString(bool colors) : Stream(0, 4, eByteOrderBig, colors) {} + +StreamString::StreamString(uint32_t flags, uint32_t addr_size, + ByteOrder byte_order) + : Stream(flags, addr_size, byte_order), m_packet() {} + +StreamString::~StreamString() = default; + +void StreamString::Flush() { + // Nothing to do when flushing a buffer based stream... +} + +size_t StreamString::WriteImpl(const void *s, size_t length) { + m_packet.append(static_cast<const char *>(s), length); + return length; +} + +void StreamString::Clear() { + m_packet.clear(); + m_bytes_written = 0; +} + +bool StreamString::Empty() const { return GetSize() == 0; } + +size_t StreamString::GetSize() const { return m_packet.size(); } + +size_t StreamString::GetSizeOfLastLine() const { + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) { + return length; + } else { + ++last_line_begin_pos; + return length - last_line_begin_pos; + } +} + +llvm::StringRef StreamString::GetString() const { return m_packet; } + +void StreamString::FillLastLineToColumn(uint32_t column, char fill_char) { + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) { + last_line_begin_pos = 0; + } else { + ++last_line_begin_pos; + } + + const size_t line_columns = length - last_line_begin_pos; + if (column > line_columns) { + m_packet.append(column - line_columns, fill_char); + } +} diff --git a/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp b/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp new file mode 100644 index 000000000000..579faa3da42f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp @@ -0,0 +1,369 @@ +//===-- StringExtractor.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/Utility/StringExtractor.h" +#include "llvm/ADT/StringExtras.h" + +#include <tuple> + +#include <cctype> +#include <cstdlib> +#include <cstring> + +static inline int xdigit_to_sint(char ch) { + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + if (ch >= 'A' && ch <= 'F') + return 10 + ch - 'A'; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return -1; +} + +// StringExtractor constructor +StringExtractor::StringExtractor() : m_packet() {} + +StringExtractor::StringExtractor(llvm::StringRef packet_str) : m_packet() { + m_packet.assign(packet_str.begin(), packet_str.end()); +} + +StringExtractor::StringExtractor(const char *packet_cstr) : m_packet() { + if (packet_cstr) + m_packet.assign(packet_cstr); +} + +// Destructor +StringExtractor::~StringExtractor() = default; + +char StringExtractor::GetChar(char fail_value) { + if (m_index < m_packet.size()) { + char ch = m_packet[m_index]; + ++m_index; + return ch; + } + m_index = UINT64_MAX; + return fail_value; +} + +// If a pair of valid hex digits exist at the head of the StringExtractor they +// are decoded into an unsigned byte and returned by this function +// +// If there is not a pair of valid hex digits at the head of the +// StringExtractor, it is left unchanged and -1 is returned +int StringExtractor::DecodeHexU8() { + SkipSpaces(); + if (GetBytesLeft() < 2) { + return -1; + } + const int hi_nibble = xdigit_to_sint(m_packet[m_index]); + const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]); + if (hi_nibble == -1 || lo_nibble == -1) { + return -1; + } + m_index += 2; + return static_cast<uint8_t>((hi_nibble << 4) + lo_nibble); +} + +// Extract an unsigned character from two hex ASCII chars in the packet string, +// or return fail_value on failure +uint8_t StringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) { + // On success, fail_value will be overwritten with the next character in the + // stream + GetHexU8Ex(fail_value, set_eof_on_fail); + return fail_value; +} + +bool StringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) { + int byte = DecodeHexU8(); + if (byte == -1) { + if (set_eof_on_fail || m_index >= m_packet.size()) + m_index = UINT64_MAX; + // ch should not be changed in case of failure + return false; + } + ch = static_cast<uint8_t>(byte); + return true; +} + +uint32_t StringExtractor::GetU32(uint32_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base)); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int32_t StringExtractor::GetS32(int32_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base)); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +uint64_t StringExtractor::GetU64(uint64_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + uint64_t result = ::strtoull(cstr, &end, base); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int64_t StringExtractor::GetS64(int64_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int64_t result = ::strtoll(cstr, &end, base); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +uint32_t StringExtractor::GetHexMaxU32(bool little_endian, + uint32_t fail_value) { + uint32_t result = 0; + uint32_t nibble_count = 0; + + SkipSpaces(); + if (little_endian) { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + nibble_lo = xdigit_to_sint(m_packet[m_index]); + ++m_index; + result |= (static_cast<uint32_t>(nibble_hi) << (shift_amount + 4)); + result |= (static_cast<uint32_t>(nibble_lo) << shift_amount); + nibble_count += 2; + shift_amount += 8; + } else { + result |= (static_cast<uint32_t>(nibble_hi) << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + } + } else { + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint(m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +uint64_t StringExtractor::GetHexMaxU64(bool little_endian, + uint64_t fail_value) { + uint64_t result = 0; + uint32_t nibble_count = 0; + + SkipSpaces(); + if (little_endian) { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + nibble_lo = xdigit_to_sint(m_packet[m_index]); + ++m_index; + result |= (static_cast<uint64_t>(nibble_hi) << (shift_amount + 4)); + result |= (static_cast<uint64_t>(nibble_lo) << shift_amount); + nibble_count += 2; + shift_amount += 8; + } else { + result |= (static_cast<uint64_t>(nibble_hi) << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + } + } else { + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint(m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +bool StringExtractor::ConsumeFront(const llvm::StringRef &str) { + llvm::StringRef S = GetStringRef(); + if (!S.starts_with(str)) + return false; + else + m_index += str.size(); + return true; +} + +size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef<uint8_t> dest, + uint8_t fail_fill_value) { + size_t bytes_extracted = 0; + while (!dest.empty() && GetBytesLeft() > 0) { + dest[0] = GetHexU8(fail_fill_value); + if (!IsGood()) + break; + ++bytes_extracted; + dest = dest.drop_front(); + } + + if (!dest.empty()) + ::memset(dest.data(), fail_fill_value, dest.size()); + + return bytes_extracted; +} + +// Decodes all valid hex encoded bytes at the head of the StringExtractor, +// limited by dst_len. +// +// Returns the number of bytes successfully decoded +size_t StringExtractor::GetHexBytesAvail(llvm::MutableArrayRef<uint8_t> dest) { + size_t bytes_extracted = 0; + while (!dest.empty()) { + int decode = DecodeHexU8(); + if (decode == -1) + break; + dest[0] = static_cast<uint8_t>(decode); + dest = dest.drop_front(); + ++bytes_extracted; + } + return bytes_extracted; +} + +size_t StringExtractor::GetHexByteString(std::string &str) { + str.clear(); + str.reserve(GetBytesLeft() / 2); + char ch; + while ((ch = GetHexU8()) != '\0') + str.append(1, ch); + return str.size(); +} + +size_t StringExtractor::GetHexByteStringFixedLength(std::string &str, + uint32_t nibble_length) { + str.clear(); + + uint32_t nibble_count = 0; + for (const char *pch = Peek(); + (nibble_count < nibble_length) && (pch != nullptr); + str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) { + } + + return str.size(); +} + +size_t StringExtractor::GetHexByteStringTerminatedBy(std::string &str, + char terminator) { + str.clear(); + char ch; + while ((ch = GetHexU8(0, false)) != '\0') + str.append(1, ch); + if (Peek() && *Peek() == terminator) + return str.size(); + + str.clear(); + return str.size(); +} + +bool StringExtractor::GetNameColonValue(llvm::StringRef &name, + llvm::StringRef &value) { + // Read something in the form of NNNN:VVVV; where NNNN is any character that + // is not a colon, followed by a ':' character, then a value (one or more ';' + // chars), followed by a ';' + if (m_index >= m_packet.size()) + return fail(); + + llvm::StringRef view(m_packet); + if (view.empty()) + return fail(); + + llvm::StringRef a, b, c, d; + view = view.substr(m_index); + std::tie(a, b) = view.split(':'); + if (a.empty() || b.empty()) + return fail(); + std::tie(c, d) = b.split(';'); + if (b == c && d.empty()) + return fail(); + + name = a; + value = c; + if (d.empty()) + m_index = m_packet.size(); + else { + size_t bytes_consumed = d.data() - view.data(); + m_index += bytes_consumed; + } + return true; +} + +void StringExtractor::SkipSpaces() { + const size_t n = m_packet.size(); + while (m_index < n && llvm::isSpace(m_packet[m_index])) + ++m_index; +} diff --git a/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp new file mode 100644 index 000000000000..9f79d2271b1e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -0,0 +1,682 @@ +//===-- StringExtractorGDBRemote.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/Utility/StringExtractorGDBRemote.h" + +#include <cctype> +#include <cstring> +#include <optional> + +constexpr lldb::pid_t StringExtractorGDBRemote::AllProcesses; +constexpr lldb::tid_t StringExtractorGDBRemote::AllThreads; + +StringExtractorGDBRemote::ResponseType +StringExtractorGDBRemote::GetResponseType() const { + if (m_packet.empty()) + return eUnsupported; + + switch (m_packet[0]) { + case 'E': + if (isxdigit(m_packet[1]) && isxdigit(m_packet[2])) { + if (m_packet.size() == 3) + return eError; + llvm::StringRef packet_ref(m_packet); + if (packet_ref[3] == ';') { + auto err_string = packet_ref.substr(4); + for (auto e : err_string) + if (!isxdigit(e)) + return eResponse; + return eError; + } + } + break; + + case 'O': + if (m_packet.size() == 2 && m_packet[1] == 'K') + return eOK; + break; + + case '+': + if (m_packet.size() == 1) + return eAck; + break; + + case '-': + if (m_packet.size() == 1) + return eNack; + break; + } + return eResponse; +} + +StringExtractorGDBRemote::ServerPacketType +StringExtractorGDBRemote::GetServerPacketType() const { +#define PACKET_MATCHES(s) \ + ((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0)) +#define PACKET_STARTS_WITH(s) \ + ((packet_size >= (sizeof(s) - 1)) && \ + ::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0) + + // Empty is not a supported packet... + if (m_packet.empty()) + return eServerPacketType_invalid; + + const size_t packet_size = m_packet.size(); + const char *packet_cstr = m_packet.c_str(); + switch (m_packet[0]) { + + case '%': + return eServerPacketType_notify; + + case '\x03': + if (packet_size == 1) + return eServerPacketType_interrupt; + break; + + case '-': + if (packet_size == 1) + return eServerPacketType_nack; + break; + + case '+': + if (packet_size == 1) + return eServerPacketType_ack; + break; + + case 'A': + return eServerPacketType_A; + + case 'Q': + + switch (packet_cstr[1]) { + case 'E': + if (PACKET_STARTS_WITH("QEnvironment:")) + return eServerPacketType_QEnvironment; + if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:")) + return eServerPacketType_QEnvironmentHexEncoded; + if (PACKET_STARTS_WITH("QEnableErrorStrings")) + return eServerPacketType_QEnableErrorStrings; + break; + + case 'P': + if (PACKET_STARTS_WITH("QPassSignals:")) + return eServerPacketType_QPassSignals; + break; + + case 'S': + if (PACKET_MATCHES("QStartNoAckMode")) + return eServerPacketType_QStartNoAckMode; + if (PACKET_STARTS_WITH("QSaveRegisterState")) + return eServerPacketType_QSaveRegisterState; + if (PACKET_STARTS_WITH("QSetDisableASLR:")) + return eServerPacketType_QSetDisableASLR; + if (PACKET_STARTS_WITH("QSetDetachOnError:")) + return eServerPacketType_QSetDetachOnError; + if (PACKET_STARTS_WITH("QSetSTDIN:")) + return eServerPacketType_QSetSTDIN; + if (PACKET_STARTS_WITH("QSetSTDOUT:")) + return eServerPacketType_QSetSTDOUT; + if (PACKET_STARTS_WITH("QSetSTDERR:")) + return eServerPacketType_QSetSTDERR; + if (PACKET_STARTS_WITH("QSetWorkingDir:")) + return eServerPacketType_QSetWorkingDir; + if (PACKET_STARTS_WITH("QSetLogging:")) + return eServerPacketType_QSetLogging; + if (PACKET_STARTS_WITH("QSetIgnoredExceptions")) + return eServerPacketType_QSetIgnoredExceptions; + if (PACKET_STARTS_WITH("QSetMaxPacketSize:")) + return eServerPacketType_QSetMaxPacketSize; + if (PACKET_STARTS_WITH("QSetMaxPayloadSize:")) + return eServerPacketType_QSetMaxPayloadSize; + if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;")) + return eServerPacketType_QSetEnableAsyncProfiling; + if (PACKET_STARTS_WITH("QSyncThreadState:")) + return eServerPacketType_QSyncThreadState; + break; + + case 'L': + if (PACKET_STARTS_WITH("QLaunchArch:")) + return eServerPacketType_QLaunchArch; + if (PACKET_MATCHES("QListThreadsInStopReply")) + return eServerPacketType_QListThreadsInStopReply; + break; + + case 'M': + if (PACKET_STARTS_WITH("QMemTags")) + return eServerPacketType_QMemTags; + break; + + case 'N': + if (PACKET_STARTS_WITH("QNonStop:")) + return eServerPacketType_QNonStop; + break; + + case 'R': + if (PACKET_STARTS_WITH("QRestoreRegisterState:")) + return eServerPacketType_QRestoreRegisterState; + break; + + case 'T': + if (PACKET_MATCHES("QThreadSuffixSupported")) + return eServerPacketType_QThreadSuffixSupported; + break; + } + break; + + case 'q': + switch (packet_cstr[1]) { + case 's': + if (PACKET_MATCHES("qsProcessInfo")) + return eServerPacketType_qsProcessInfo; + if (PACKET_MATCHES("qsThreadInfo")) + return eServerPacketType_qsThreadInfo; + break; + + case 'f': + if (PACKET_STARTS_WITH("qfProcessInfo")) + return eServerPacketType_qfProcessInfo; + if (PACKET_STARTS_WITH("qfThreadInfo")) + return eServerPacketType_qfThreadInfo; + break; + + case 'C': + if (packet_size == 2) + return eServerPacketType_qC; + break; + + case 'E': + if (PACKET_STARTS_WITH("qEcho:")) + return eServerPacketType_qEcho; + break; + + case 'F': + if (PACKET_STARTS_WITH("qFileLoadAddress:")) + return eServerPacketType_qFileLoadAddress; + break; + + case 'G': + if (PACKET_STARTS_WITH("qGroupName:")) + return eServerPacketType_qGroupName; + if (PACKET_MATCHES("qGetWorkingDir")) + return eServerPacketType_qGetWorkingDir; + if (PACKET_MATCHES("qGetPid")) + return eServerPacketType_qGetPid; + if (PACKET_STARTS_WITH("qGetProfileData;")) + return eServerPacketType_qGetProfileData; + if (PACKET_MATCHES("qGDBServerVersion")) + return eServerPacketType_qGDBServerVersion; + break; + + case 'H': + if (PACKET_MATCHES("qHostInfo")) + return eServerPacketType_qHostInfo; + break; + + case 'K': + if (PACKET_STARTS_WITH("qKillSpawnedProcess")) + return eServerPacketType_qKillSpawnedProcess; + break; + + case 'L': + if (PACKET_STARTS_WITH("qLaunchGDBServer")) + return eServerPacketType_qLaunchGDBServer; + if (PACKET_MATCHES("qLaunchSuccess")) + return eServerPacketType_qLaunchSuccess; + break; + + case 'M': + if (PACKET_STARTS_WITH("qMemoryRegionInfo:")) + return eServerPacketType_qMemoryRegionInfo; + if (PACKET_MATCHES("qMemoryRegionInfo")) + return eServerPacketType_qMemoryRegionInfoSupported; + if (PACKET_STARTS_WITH("qModuleInfo:")) + return eServerPacketType_qModuleInfo; + if (PACKET_STARTS_WITH("qMemTags:")) + return eServerPacketType_qMemTags; + break; + + case 'P': + if (PACKET_STARTS_WITH("qProcessInfoPID:")) + return eServerPacketType_qProcessInfoPID; + if (PACKET_STARTS_WITH("qPlatform_shell:")) + return eServerPacketType_qPlatform_shell; + if (PACKET_STARTS_WITH("qPlatform_mkdir:")) + return eServerPacketType_qPlatform_mkdir; + if (PACKET_STARTS_WITH("qPlatform_chmod:")) + return eServerPacketType_qPlatform_chmod; + if (PACKET_MATCHES("qProcessInfo")) + return eServerPacketType_qProcessInfo; + if (PACKET_STARTS_WITH("qPathComplete:")) + return eServerPacketType_qPathComplete; + break; + + case 'Q': + if (PACKET_MATCHES("qQueryGDBServer")) + return eServerPacketType_qQueryGDBServer; + break; + + case 'R': + if (PACKET_STARTS_WITH("qRcmd,")) + return eServerPacketType_qRcmd; + if (PACKET_STARTS_WITH("qRegisterInfo")) + return eServerPacketType_qRegisterInfo; + break; + + case 'S': + if (PACKET_STARTS_WITH("qSaveCore")) + return eServerPacketType_qLLDBSaveCore; + if (PACKET_STARTS_WITH("qSpeedTest:")) + return eServerPacketType_qSpeedTest; + if (PACKET_MATCHES("qShlibInfoAddr")) + return eServerPacketType_qShlibInfoAddr; + if (PACKET_MATCHES("qStepPacketSupported")) + return eServerPacketType_qStepPacketSupported; + if (PACKET_STARTS_WITH("qSupported")) + return eServerPacketType_qSupported; + if (PACKET_MATCHES("qSyncThreadStateSupported")) + return eServerPacketType_qSyncThreadStateSupported; + break; + + case 'T': + if (PACKET_STARTS_WITH("qThreadExtraInfo,")) + return eServerPacketType_qThreadExtraInfo; + if (PACKET_STARTS_WITH("qThreadStopInfo")) + return eServerPacketType_qThreadStopInfo; + break; + + case 'U': + if (PACKET_STARTS_WITH("qUserName:")) + return eServerPacketType_qUserName; + break; + + case 'V': + if (PACKET_MATCHES("qVAttachOrWaitSupported")) + return eServerPacketType_qVAttachOrWaitSupported; + break; + + case 'W': + if (PACKET_STARTS_WITH("qWatchpointSupportInfo:")) + return eServerPacketType_qWatchpointSupportInfo; + if (PACKET_MATCHES("qWatchpointSupportInfo")) + return eServerPacketType_qWatchpointSupportInfoSupported; + break; + + case 'X': + if (PACKET_STARTS_WITH("qXfer:")) + return eServerPacketType_qXfer; + break; + } + break; + + case 'j': + if (PACKET_STARTS_WITH("jModulesInfo:")) + return eServerPacketType_jModulesInfo; + if (PACKET_MATCHES("jSignalsInfo")) + return eServerPacketType_jSignalsInfo; + if (PACKET_MATCHES("jThreadsInfo")) + return eServerPacketType_jThreadsInfo; + + if (PACKET_MATCHES("jLLDBTraceSupported")) + return eServerPacketType_jLLDBTraceSupported; + if (PACKET_STARTS_WITH("jLLDBTraceStop:")) + return eServerPacketType_jLLDBTraceStop; + if (PACKET_STARTS_WITH("jLLDBTraceStart:")) + return eServerPacketType_jLLDBTraceStart; + if (PACKET_STARTS_WITH("jLLDBTraceGetState:")) + return eServerPacketType_jLLDBTraceGetState; + if (PACKET_STARTS_WITH("jLLDBTraceGetBinaryData:")) + return eServerPacketType_jLLDBTraceGetBinaryData; + break; + + case 'v': + if (PACKET_STARTS_WITH("vFile:")) { + if (PACKET_STARTS_WITH("vFile:open:")) + return eServerPacketType_vFile_open; + else if (PACKET_STARTS_WITH("vFile:close:")) + return eServerPacketType_vFile_close; + else if (PACKET_STARTS_WITH("vFile:pread")) + return eServerPacketType_vFile_pread; + else if (PACKET_STARTS_WITH("vFile:pwrite")) + return eServerPacketType_vFile_pwrite; + else if (PACKET_STARTS_WITH("vFile:size")) + return eServerPacketType_vFile_size; + else if (PACKET_STARTS_WITH("vFile:exists")) + return eServerPacketType_vFile_exists; + else if (PACKET_STARTS_WITH("vFile:fstat")) + return eServerPacketType_vFile_fstat; + else if (PACKET_STARTS_WITH("vFile:stat")) + return eServerPacketType_vFile_stat; + else if (PACKET_STARTS_WITH("vFile:mode")) + return eServerPacketType_vFile_mode; + else if (PACKET_STARTS_WITH("vFile:MD5")) + return eServerPacketType_vFile_md5; + else if (PACKET_STARTS_WITH("vFile:symlink")) + return eServerPacketType_vFile_symlink; + else if (PACKET_STARTS_WITH("vFile:unlink")) + return eServerPacketType_vFile_unlink; + + } else { + if (PACKET_STARTS_WITH("vAttach;")) + return eServerPacketType_vAttach; + if (PACKET_STARTS_WITH("vAttachWait;")) + return eServerPacketType_vAttachWait; + if (PACKET_STARTS_WITH("vAttachOrWait;")) + return eServerPacketType_vAttachOrWait; + if (PACKET_STARTS_WITH("vAttachName;")) + return eServerPacketType_vAttachName; + if (PACKET_STARTS_WITH("vCont;")) + return eServerPacketType_vCont; + if (PACKET_MATCHES("vCont?")) + return eServerPacketType_vCont_actions; + if (PACKET_STARTS_WITH("vKill;")) + return eServerPacketType_vKill; + if (PACKET_STARTS_WITH("vRun;")) + return eServerPacketType_vRun; + if (PACKET_MATCHES("vStopped")) + return eServerPacketType_vStopped; + if (PACKET_MATCHES("vCtrlC")) + return eServerPacketType_vCtrlC; + if (PACKET_MATCHES("vStdio")) + return eServerPacketType_vStdio; + break; + + } + break; + case '_': + switch (packet_cstr[1]) { + case 'M': + return eServerPacketType__M; + + case 'm': + return eServerPacketType__m; + } + break; + + case '?': + if (packet_size == 1) + return eServerPacketType_stop_reason; + break; + + case 'c': + return eServerPacketType_c; + + case 'C': + return eServerPacketType_C; + + case 'D': + return eServerPacketType_D; + + case 'g': + return eServerPacketType_g; + + case 'G': + return eServerPacketType_G; + + case 'H': + return eServerPacketType_H; + + case 'I': + return eServerPacketType_I; + + case 'k': + if (packet_size == 1) + return eServerPacketType_k; + break; + + case 'm': + return eServerPacketType_m; + + case 'M': + return eServerPacketType_M; + + case 'p': + return eServerPacketType_p; + + case 'P': + return eServerPacketType_P; + + case 's': + if (packet_size == 1) + return eServerPacketType_s; + break; + + case 'S': + return eServerPacketType_S; + + case 'x': + return eServerPacketType_x; + + case 'X': + return eServerPacketType_X; + + case 'T': + return eServerPacketType_T; + + case 'z': + if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') + return eServerPacketType_z; + break; + + case 'Z': + if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') + return eServerPacketType_Z; + break; + } + return eServerPacketType_unimplemented; +} + +bool StringExtractorGDBRemote::IsOKResponse() const { + return GetResponseType() == eOK; +} + +bool StringExtractorGDBRemote::IsUnsupportedResponse() const { + return GetResponseType() == eUnsupported; +} + +bool StringExtractorGDBRemote::IsNormalResponse() const { + return GetResponseType() == eResponse; +} + +bool StringExtractorGDBRemote::IsErrorResponse() const { + return GetResponseType() == eError && isxdigit(m_packet[1]) && + isxdigit(m_packet[2]); +} + +uint8_t StringExtractorGDBRemote::GetError() { + if (GetResponseType() == eError) { + SetFilePos(1); + return GetHexU8(255); + } + return 0; +} + +lldb_private::Status StringExtractorGDBRemote::GetStatus() { + lldb_private::Status error; + if (GetResponseType() == eError) { + SetFilePos(1); + uint8_t errc = GetHexU8(255); + error.SetError(errc, lldb::eErrorTypeGeneric); + + error.SetErrorStringWithFormat("Error %u", errc); + std::string error_messg; + if (GetChar() == ';') { + GetHexByteString(error_messg); + error.SetErrorString(error_messg); + } + } + return error; +} + +size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) { + // Just get the data bytes in the string as + // GDBRemoteCommunication::CheckForPacket() already removes any 0x7d escaped + // characters. If any 0x7d characters are left in the packet, then they are + // supposed to be there... + str.clear(); + const size_t bytes_left = GetBytesLeft(); + if (bytes_left > 0) { + str.assign(m_packet, m_index, bytes_left); + m_index += bytes_left; + } + return str.size(); +} + +static bool +OKErrorNotSupportedResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eError: + case StringExtractorGDBRemote::eUnsupported: + return true; + + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + case StringExtractorGDBRemote::eResponse: + break; + } + return false; +} + +static bool JSONResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eUnsupported: + case StringExtractorGDBRemote::eError: + return true; // Accept unsupported or EXX as valid responses + + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + break; + + case StringExtractorGDBRemote::eResponse: + // JSON that is returned in from JSON query packets is currently always + // either a dictionary which starts with a '{', or an array which starts + // with a '['. This is a quick validator to just make sure the response + // could be valid JSON without having to validate all of the + // JSON content. + switch (response.GetStringRef()[0]) { + case '{': + return true; + case '[': + return true; + default: + break; + } + break; + } + return false; +} + +static bool +ASCIIHexBytesResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eUnsupported: + case StringExtractorGDBRemote::eError: + return true; // Accept unsupported or EXX as valid responses + + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + break; + + case StringExtractorGDBRemote::eResponse: { + uint32_t valid_count = 0; + for (const char ch : response.GetStringRef()) { + if (!isxdigit(ch)) { + return false; + } + if (++valid_count >= 16) + break; // Don't validate all the characters in case the packet is very + // large + } + return true; + } break; + } + return false; +} + +void StringExtractorGDBRemote::CopyResponseValidator( + const StringExtractorGDBRemote &rhs) { + m_validator = rhs.m_validator; + m_validator_baton = rhs.m_validator_baton; +} + +void StringExtractorGDBRemote::SetResponseValidator( + ResponseValidatorCallback callback, void *baton) { + m_validator = callback; + m_validator_baton = baton; +} + +void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() { + m_validator = OKErrorNotSupportedResponseValidator; + m_validator_baton = nullptr; +} + +void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() { + m_validator = ASCIIHexBytesResponseValidator; + m_validator_baton = nullptr; +} + +void StringExtractorGDBRemote::SetResponseValidatorToJSON() { + m_validator = JSONResponseValidator; + m_validator_baton = nullptr; +} + +bool StringExtractorGDBRemote::ValidateResponse() const { + // If we have a validator callback, try to validate the callback + if (m_validator) + return m_validator(m_validator_baton, *this); + else + return true; // No validator, so response is valid +} + +std::optional<std::pair<lldb::pid_t, lldb::tid_t>> +StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) { + llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index); + size_t initial_length = view.size(); + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + lldb::tid_t tid; + + if (view.consume_front("p")) { + // process identifier + if (view.consume_front("-1")) { + // -1 is a special case + pid = AllProcesses; + } else if (view.consumeInteger(16, pid) || pid == 0) { + // not a valid hex integer OR unsupported pid 0 + m_index = UINT64_MAX; + return std::nullopt; + } + + // "." must follow if we expect TID too; otherwise, we assume -1 + if (!view.consume_front(".")) { + // update m_index + m_index += initial_length - view.size(); + + return {{pid, AllThreads}}; + } + } + + // thread identifier + if (view.consume_front("-1")) { + // -1 is a special case + tid = AllThreads; + } else if (view.consumeInteger(16, tid) || tid == 0 || pid == AllProcesses) { + // not a valid hex integer OR tid 0 OR pid -1 + a specific tid + m_index = UINT64_MAX; + return std::nullopt; + } + + // update m_index + m_index += initial_length - view.size(); + + return {{pid != LLDB_INVALID_PROCESS_ID ? pid : default_pid, tid}}; +} diff --git a/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp b/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp new file mode 100644 index 000000000000..bda6e25ce7a3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp @@ -0,0 +1,85 @@ +//===-- StringLexer.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/Utility/StringLexer.h" + +#include <algorithm> +#include <cassert> +#include <utility> + +using namespace lldb_private; + +StringLexer::StringLexer(std::string s) : m_data(std::move(s)), m_position(0) {} + +StringLexer::Character StringLexer::Peek() { return m_data[m_position]; } + +bool StringLexer::NextIf(Character c) { + auto val = Peek(); + if (val == c) { + Next(); + return true; + } + return false; +} + +std::pair<bool, StringLexer::Character> +StringLexer::NextIf(std::initializer_list<Character> cs) { + auto val = Peek(); + for (auto c : cs) { + if (val == c) { + Next(); + return {true, c}; + } + } + return {false, 0}; +} + +bool StringLexer::AdvanceIf(const std::string &token) { + auto pos = m_position; + bool matches = true; + for (auto c : token) { + if (!NextIf(c)) { + matches = false; + break; + } + } + if (!matches) { + m_position = pos; + return false; + } + return true; +} + +StringLexer::Character StringLexer::Next() { + auto val = Peek(); + Consume(); + return val; +} + +bool StringLexer::HasAtLeast(Size s) { + return (m_data.size() - m_position) >= s; +} + +void StringLexer::PutBack(Size s) { + assert(m_position >= s); + m_position -= s; +} + +std::string StringLexer::GetUnlexed() { + return std::string(m_data, m_position); +} + +void StringLexer::Consume() { m_position++; } + +StringLexer &StringLexer::operator=(const StringLexer &rhs) { + if (this != &rhs) { + m_data = rhs.m_data; + m_position = rhs.m_position; + } + return *this; +} diff --git a/contrib/llvm-project/lldb/source/Utility/StringList.cpp b/contrib/llvm-project/lldb/source/Utility/StringList.cpp new file mode 100644 index 000000000000..98923a07db70 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StringList.cpp @@ -0,0 +1,247 @@ +//===-- StringList.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/Utility/StringList.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/ArrayRef.h" + +#include <algorithm> +#include <cstdint> +#include <cstring> + +using namespace lldb_private; + +StringList::StringList() : m_strings() {} + +StringList::StringList(const char *str) : m_strings() { + if (str) + m_strings.push_back(str); +} + +StringList::StringList(const char **strv, int strc) : m_strings() { + for (int i = 0; i < strc; ++i) { + if (strv[i]) + m_strings.push_back(strv[i]); + } +} + +StringList::~StringList() = default; + +void StringList::AppendString(const char *str) { + if (str) + m_strings.push_back(str); +} + +void StringList::AppendString(const std::string &s) { m_strings.push_back(s); } + +void StringList::AppendString(std::string &&s) { + m_strings.push_back(std::move(s)); +} + +void StringList::AppendString(const char *str, size_t str_len) { + if (str) + m_strings.push_back(std::string(str, str_len)); +} + +void StringList::AppendString(llvm::StringRef str) { + m_strings.push_back(str.str()); +} + +void StringList::AppendString(const llvm::Twine &str) { + m_strings.push_back(str.str()); +} + +void StringList::AppendList(const char **strv, int strc) { + for (int i = 0; i < strc; ++i) { + if (strv[i]) + m_strings.push_back(strv[i]); + } +} + +void StringList::AppendList(StringList strings) { + m_strings.reserve(m_strings.size() + strings.GetSize()); + m_strings.insert(m_strings.end(), strings.begin(), strings.end()); +} + +size_t StringList::GetSize() const { return m_strings.size(); } + +size_t StringList::GetMaxStringLength() const { + size_t max_length = 0; + for (const auto &s : m_strings) { + const size_t len = s.size(); + if (max_length < len) + max_length = len; + } + return max_length; +} + +const char *StringList::GetStringAtIndex(size_t idx) const { + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return nullptr; +} + +void StringList::Join(const char *separator, Stream &strm) { + size_t size = GetSize(); + + if (size == 0) + return; + + for (uint32_t i = 0; i < size; ++i) { + if (i > 0) + strm.PutCString(separator); + strm.PutCString(GetStringAtIndex(i)); + } +} + +void StringList::Clear() { m_strings.clear(); } + +std::string StringList::LongestCommonPrefix() { + if (m_strings.empty()) + return {}; + + auto args = llvm::ArrayRef(m_strings); + llvm::StringRef prefix = args.front(); + for (auto arg : args.drop_front()) { + size_t count = 0; + for (count = 0; count < std::min(prefix.size(), arg.size()); ++count) { + if (prefix[count] != arg[count]) + break; + } + prefix = prefix.take_front(count); + } + return prefix.str(); +} + +void StringList::InsertStringAtIndex(size_t idx, const char *str) { + if (str) { + if (idx < m_strings.size()) + m_strings.insert(m_strings.begin() + idx, str); + else + m_strings.push_back(str); + } +} + +void StringList::InsertStringAtIndex(size_t idx, const std::string &str) { + if (idx < m_strings.size()) + m_strings.insert(m_strings.begin() + idx, str); + else + m_strings.push_back(str); +} + +void StringList::InsertStringAtIndex(size_t idx, std::string &&str) { + if (idx < m_strings.size()) + m_strings.insert(m_strings.begin() + idx, std::move(str)); + else + m_strings.push_back(std::move(str)); +} + +void StringList::DeleteStringAtIndex(size_t idx) { + if (idx < m_strings.size()) + m_strings.erase(m_strings.begin() + idx); +} + +size_t StringList::SplitIntoLines(const std::string &lines) { + return SplitIntoLines(lines.c_str(), lines.size()); +} + +size_t StringList::SplitIntoLines(const char *lines, size_t len) { + const size_t orig_size = m_strings.size(); + + if (len == 0) + return 0; + + const char *k_newline_chars = "\r\n"; + const char *p = lines; + const char *end = lines + len; + while (p < end) { + size_t count = strcspn(p, k_newline_chars); + if (count == 0) { + if (p[count] == '\r' || p[count] == '\n') + m_strings.push_back(std::string()); + else + break; + } else { + if (p + count > end) + count = end - p; + m_strings.push_back(std::string(p, count)); + } + if (p[count] == '\r' && p[count + 1] == '\n') + count++; // Skip an extra newline char for the DOS newline + count++; // Skip the newline character + p += count; + } + return m_strings.size() - orig_size; +} + +void StringList::RemoveBlankLines() { + if (GetSize() == 0) + return; + + size_t idx = 0; + while (idx < m_strings.size()) { + if (m_strings[idx].empty()) + DeleteStringAtIndex(idx); + else + idx++; + } +} + +std::string StringList::CopyList(const char *item_preamble, + const char *items_sep) const { + StreamString strm; + for (size_t i = 0; i < GetSize(); i++) { + if (i && items_sep && items_sep[0]) + strm << items_sep; + if (item_preamble) + strm << item_preamble; + strm << GetStringAtIndex(i); + } + return std::string(strm.GetString()); +} + +StringList &StringList::operator<<(const char *str) { + AppendString(str); + return *this; +} + +StringList &StringList::operator<<(const std::string &str) { + AppendString(str); + return *this; +} + +StringList &StringList::operator<<(const StringList &strings) { + AppendList(strings); + return *this; +} + +StringList &StringList::operator=(const std::vector<std::string> &rhs) { + m_strings.assign(rhs.begin(), rhs.end()); + + return *this; +} + +void StringList::LogDump(Log *log, const char *name) { + if (!log) + return; + + StreamString strm; + if (name) + strm.Printf("Begin %s:\n", name); + for (const auto &s : m_strings) { + strm.Indent(); + strm.Printf("%s\n", s.c_str()); + } + if (name) + strm.Printf("End %s.\n", name); + + LLDB_LOGV(log, "{0}", strm.GetData()); +} diff --git a/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp b/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp new file mode 100644 index 000000000000..7686d052c599 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp @@ -0,0 +1,291 @@ +//===-- StructuredData.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/Utility/StructuredData.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cerrno> +#include <cinttypes> +#include <cstdlib> + +using namespace lldb_private; +using namespace llvm; + +static StructuredData::ObjectSP ParseJSONValue(json::Value &value); +static StructuredData::ObjectSP ParseJSONObject(json::Object *object); +static StructuredData::ObjectSP ParseJSONArray(json::Array *array); + +StructuredData::ObjectSP StructuredData::ParseJSON(llvm::StringRef json_text) { + llvm::Expected<json::Value> value = json::parse(json_text); + if (!value) { + llvm::consumeError(value.takeError()); + return nullptr; + } + return ParseJSONValue(*value); +} + +StructuredData::ObjectSP +StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) { + StructuredData::ObjectSP return_sp; + + auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath()); + if (!buffer_or_error) { + error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.", + input_spec.GetPath(), + buffer_or_error.getError().message()); + return return_sp; + } + llvm::Expected<json::Value> value = + json::parse(buffer_or_error.get()->getBuffer().str()); + if (value) + return ParseJSONValue(*value); + error.SetErrorString(toString(value.takeError())); + return StructuredData::ObjectSP(); +} + +bool StructuredData::IsRecordType(const ObjectSP object_sp) { + return object_sp->GetType() == lldb::eStructuredDataTypeArray || + object_sp->GetType() == lldb::eStructuredDataTypeDictionary; +} + +static StructuredData::ObjectSP ParseJSONValue(json::Value &value) { + if (json::Object *O = value.getAsObject()) + return ParseJSONObject(O); + + if (json::Array *A = value.getAsArray()) + return ParseJSONArray(A); + + if (auto s = value.getAsString()) + return std::make_shared<StructuredData::String>(*s); + + if (auto b = value.getAsBoolean()) + return std::make_shared<StructuredData::Boolean>(*b); + + if (auto u = value.getAsUINT64()) + return std::make_shared<StructuredData::UnsignedInteger>(*u); + + if (auto i = value.getAsInteger()) + return std::make_shared<StructuredData::SignedInteger>(*i); + + if (auto d = value.getAsNumber()) + return std::make_shared<StructuredData::Float>(*d); + + if (auto n = value.getAsNull()) + return std::make_shared<StructuredData::Null>(); + + return StructuredData::ObjectSP(); +} + +static StructuredData::ObjectSP ParseJSONObject(json::Object *object) { + auto dict_up = std::make_unique<StructuredData::Dictionary>(); + for (auto &KV : *object) { + StringRef key = KV.first; + json::Value value = KV.second; + if (StructuredData::ObjectSP value_sp = ParseJSONValue(value)) + dict_up->AddItem(key, value_sp); + } + return std::move(dict_up); +} + +static StructuredData::ObjectSP ParseJSONArray(json::Array *array) { + auto array_up = std::make_unique<StructuredData::Array>(); + for (json::Value &value : *array) { + if (StructuredData::ObjectSP value_sp = ParseJSONValue(value)) + array_up->AddItem(value_sp); + } + return std::move(array_up); +} + +StructuredData::ObjectSP +StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) { + if (GetType() == lldb::eStructuredDataTypeDictionary) { + std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.'); + llvm::StringRef key = match.first; + ObjectSP value = GetAsDictionary()->GetValueForKey(key); + if (!value) + return {}; + + // Do we have additional words to descend? If not, return the value + // we're at right now. + if (match.second.empty()) + return value; + + return value->GetObjectForDotSeparatedPath(match.second); + } + + if (GetType() == lldb::eStructuredDataTypeArray) { + std::pair<llvm::StringRef, llvm::StringRef> match = path.split('['); + if (match.second.empty()) + return shared_from_this(); + + uint64_t val = 0; + if (!llvm::to_integer(match.second, val, /* Base = */ 10)) + return {}; + + return GetAsArray()->GetItemAtIndex(val); + } + + return shared_from_this(); +} + +void StructuredData::Object::DumpToStdout(bool pretty_print) const { + json::OStream stream(llvm::outs(), pretty_print ? 2 : 0); + Serialize(stream); +} + +void StructuredData::Array::Serialize(json::OStream &s) const { + s.arrayBegin(); + for (const auto &item_sp : m_items) { + item_sp->Serialize(s); + } + s.arrayEnd(); +} + +void StructuredData::Float::Serialize(json::OStream &s) const { + s.value(m_value); +} + +void StructuredData::Boolean::Serialize(json::OStream &s) const { + s.value(m_value); +} + +void StructuredData::String::Serialize(json::OStream &s) const { + s.value(m_value); +} + +void StructuredData::Dictionary::Serialize(json::OStream &s) const { + s.objectBegin(); + + // To ensure the output format is always stable, we sort the dictionary by key + // first. + using Entry = std::pair<llvm::StringRef, ObjectSP>; + std::vector<Entry> sorted_entries; + for (const auto &pair : m_dict) + sorted_entries.push_back({pair.first(), pair.second}); + + llvm::sort(sorted_entries); + + for (const auto &pair : sorted_entries) { + s.attributeBegin(pair.first); + pair.second->Serialize(s); + s.attributeEnd(); + } + s.objectEnd(); +} + +void StructuredData::Null::Serialize(json::OStream &s) const { + s.value(nullptr); +} + +void StructuredData::Generic::Serialize(json::OStream &s) const { + s.value(llvm::formatv("{0:X}", m_object)); +} + +void StructuredData::Float::GetDescription(lldb_private::Stream &s) const { + s.Printf("%f", m_value); +} + +void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const { + s.Printf(m_value ? "True" : "False"); +} + +void StructuredData::String::GetDescription(lldb_private::Stream &s) const { + s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str()); +} + +void StructuredData::Array::GetDescription(lldb_private::Stream &s) const { + size_t index = 0; + size_t indentation_level = s.GetIndentLevel(); + for (const auto &item_sp : m_items) { + // Sanitize. + if (!item_sp) + continue; + + // Reset original indentation level. + s.SetIndentLevel(indentation_level); + s.Indent(); + + // Print key + s.Printf("[%zu]:", index++); + + // Return to new line and increase indentation if value is record type. + // Otherwise add spacing. + bool should_indent = IsRecordType(item_sp); + if (should_indent) { + s.EOL(); + s.IndentMore(); + } else { + s.PutChar(' '); + } + + // Print value and new line if now last pair. + item_sp->GetDescription(s); + if (item_sp != *(--m_items.end())) + s.EOL(); + + // Reset indentation level if it was incremented previously. + if (should_indent) + s.IndentLess(); + } +} + +void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const { + size_t indentation_level = s.GetIndentLevel(); + + // To ensure the output format is always stable, we sort the dictionary by key + // first. + using Entry = std::pair<llvm::StringRef, ObjectSP>; + std::vector<Entry> sorted_entries; + for (const auto &pair : m_dict) + sorted_entries.push_back({pair.first(), pair.second}); + + llvm::sort(sorted_entries); + + for (auto iter = sorted_entries.begin(); iter != sorted_entries.end(); + iter++) { + // Sanitize. + if (iter->first.empty() || !iter->second) + continue; + + // Reset original indentation level. + s.SetIndentLevel(indentation_level); + s.Indent(); + + // Print key. + s.Format("{0}:", iter->first); + + // Return to new line and increase indentation if value is record type. + // Otherwise add spacing. + bool should_indent = IsRecordType(iter->second); + if (should_indent) { + s.EOL(); + s.IndentMore(); + } else { + s.PutChar(' '); + } + + // Print value and new line if now last pair. + iter->second->GetDescription(s); + if (std::next(iter) != sorted_entries.end()) + s.EOL(); + + // Reset indentation level if it was incremented previously. + if (should_indent) + s.IndentLess(); + } +} + +void StructuredData::Null::GetDescription(lldb_private::Stream &s) const { + s.Printf("NULL"); +} + +void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const { + s.Printf("%p", m_object); +} diff --git a/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp b/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp new file mode 100644 index 000000000000..2e334b2aae54 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp @@ -0,0 +1,94 @@ +//===-- TildeExpressionResolver.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/Utility/TildeExpressionResolver.h" + +#include <cassert> +#include <system_error> + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#if !defined(_WIN32) +#include <pwd.h> +#endif + +using namespace lldb_private; +using namespace llvm; + +namespace fs = llvm::sys::fs; +namespace path = llvm::sys::path; + +TildeExpressionResolver::~TildeExpressionResolver() = default; + +bool StandardTildeExpressionResolver::ResolveExact( + StringRef Expr, SmallVectorImpl<char> &Output) { + // We expect the tilde expression to be ONLY the expression itself, and + // contain no separators. + assert(!llvm::any_of(Expr, [](char c) { return path::is_separator(c); })); + assert(Expr.empty() || Expr[0] == '~'); + + return !fs::real_path(Expr, Output, true); +} + +bool StandardTildeExpressionResolver::ResolvePartial(StringRef Expr, + StringSet<> &Output) { + // We expect the tilde expression to be ONLY the expression itself, and + // contain no separators. + assert(!llvm::any_of(Expr, [](char c) { return path::is_separator(c); })); + assert(Expr.empty() || Expr[0] == '~'); + + Output.clear(); +#if defined(_WIN32) || defined(__ANDROID__) + return false; +#else + if (Expr.empty()) + return false; + + SmallString<32> Buffer("~"); + setpwent(); + struct passwd *user_entry; + Expr = Expr.drop_front(); + + while ((user_entry = getpwent()) != nullptr) { + StringRef ThisName(user_entry->pw_name); + if (!ThisName.starts_with(Expr)) + continue; + + Buffer.resize(1); + Buffer.append(ThisName); + Buffer.append(path::get_separator()); + Output.insert(Buffer); + } + + return true; +#endif +} + +bool TildeExpressionResolver::ResolveFullPath( + StringRef Expr, llvm::SmallVectorImpl<char> &Output) { + if (!Expr.starts_with("~")) { + Output.assign(Expr.begin(), Expr.end()); + return false; + } + + namespace path = llvm::sys::path; + StringRef Left = + Expr.take_until([](char c) { return path::is_separator(c); }); + + if (!ResolveExact(Left, Output)) { + Output.assign(Expr.begin(), Expr.end()); + return false; + } + + Output.append(Expr.begin() + Left.size(), Expr.end()); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Utility/Timer.cpp b/contrib/llvm-project/lldb/source/Utility/Timer.cpp new file mode 100644 index 000000000000..a059f877e023 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/Timer.cpp @@ -0,0 +1,161 @@ +//===-- Timer.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/Utility/Timer.h" +#include "lldb/Utility/Stream.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Signposts.h" + +#include <algorithm> +#include <map> +#include <mutex> +#include <utility> +#include <vector> + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 + +namespace { +typedef std::vector<Timer *> TimerStack; +static std::atomic<Timer::Category *> g_categories; +} // end of anonymous namespace + +/// Allows llvm::Timer to emit signposts when supported. +static llvm::ManagedStatic<llvm::SignpostEmitter> Signposts; + +std::atomic<bool> Timer::g_quiet(true); +std::atomic<unsigned> Timer::g_display_depth(0); +static std::mutex &GetFileMutex() { + static std::mutex *g_file_mutex_ptr = new std::mutex(); + return *g_file_mutex_ptr; +} + +static TimerStack &GetTimerStackForCurrentThread() { + static thread_local TimerStack g_stack; + return g_stack; +} + +Timer::Category::Category(const char *cat) : m_name(cat) { + m_nanos.store(0, std::memory_order_release); + m_nanos_total.store(0, std::memory_order_release); + m_count.store(0, std::memory_order_release); + Category *expected = g_categories; + do { + m_next = expected; + } while (!g_categories.compare_exchange_weak(expected, this)); +} + +void Timer::SetQuiet(bool value) { g_quiet = value; } + +Timer::Timer(Timer::Category &category, const char *format, ...) + : m_category(category), m_total_start(std::chrono::steady_clock::now()) { + Signposts->startInterval(this, m_category.GetName()); + TimerStack &stack = GetTimerStackForCurrentThread(); + + stack.push_back(this); + if (!g_quiet && stack.size() <= g_display_depth) { + std::lock_guard<std::mutex> lock(GetFileMutex()); + + // Indent + ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, ""); + // Print formatted string + va_list args; + va_start(args, format); + ::vfprintf(stdout, format, args); + va_end(args); + + // Newline + ::fprintf(stdout, "\n"); + } +} + +Timer::~Timer() { + using namespace std::chrono; + + auto stop_time = steady_clock::now(); + auto total_dur = stop_time - m_total_start; + auto timer_dur = total_dur - m_child_duration; + + Signposts->endInterval(this, m_category.GetName()); + + TimerStack &stack = GetTimerStackForCurrentThread(); + if (!g_quiet && stack.size() <= g_display_depth) { + std::lock_guard<std::mutex> lock(GetFileMutex()); + ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n", + int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "", + duration<double>(total_dur).count(), + duration<double>(timer_dur).count()); + } + + assert(stack.back() == this); + stack.pop_back(); + if (!stack.empty()) + stack.back()->ChildDuration(total_dur); + + // Keep total results for each category so we can dump results. + m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count(); + m_category.m_nanos_total += std::chrono::nanoseconds(total_dur).count(); + m_category.m_count++; +} + +void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; } + +/* binary function predicate: + * - returns whether a person is less than another person + */ +namespace { +struct Stats { + const char *name; + uint64_t nanos; + uint64_t nanos_total; + uint64_t count; +}; +} // namespace + +static bool CategoryMapIteratorSortCriterion(const Stats &lhs, + const Stats &rhs) { + return lhs.nanos > rhs.nanos; +} + +void Timer::ResetCategoryTimes() { + for (Category *i = g_categories; i; i = i->m_next) { + i->m_nanos.store(0, std::memory_order_release); + i->m_nanos_total.store(0, std::memory_order_release); + i->m_count.store(0, std::memory_order_release); + } +} + +void Timer::DumpCategoryTimes(Stream &s) { + std::vector<Stats> sorted; + for (Category *i = g_categories; i; i = i->m_next) { + uint64_t nanos = i->m_nanos.load(std::memory_order_acquire); + if (nanos) { + uint64_t nanos_total = i->m_nanos_total.load(std::memory_order_acquire); + uint64_t count = i->m_count.load(std::memory_order_acquire); + Stats stats{i->m_name, nanos, nanos_total, count}; + sorted.push_back(stats); + } + } + if (sorted.empty()) + return; // Later code will break without any elements. + + // Sort by time + llvm::sort(sorted, CategoryMapIteratorSortCriterion); + + for (const auto &stats : sorted) + s.Printf("%.9f sec (total: %.3fs; child: %.3fs; count: %" PRIu64 + ") for %s\n", + stats.nanos / 1000000000., stats.nanos_total / 1000000000., + (stats.nanos_total - stats.nanos) / 1000000000., stats.count, + stats.name); +} diff --git a/contrib/llvm-project/lldb/source/Utility/TraceGDBRemotePackets.cpp b/contrib/llvm-project/lldb/source/Utility/TraceGDBRemotePackets.cpp new file mode 100644 index 000000000000..387c7ef9f876 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/TraceGDBRemotePackets.cpp @@ -0,0 +1,158 @@ +//===-- TraceGDBRemotePackets.cpp -------------------------------*- C++ -*-===// +// +// 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/Utility/TraceGDBRemotePackets.h" + +using namespace llvm; +using namespace llvm::json; + +namespace lldb_private { +/// jLLDBTraceSupported +/// \{ +bool fromJSON(const json::Value &value, TraceSupportedResponse &packet, + Path path) { + ObjectMapper o(value, path); + return o && o.map("description", packet.description) && + o.map("name", packet.name); +} + +json::Value toJSON(const TraceSupportedResponse &packet) { + return json::Value( + Object{{"description", packet.description}, {"name", packet.name}}); +} +/// \} + +/// jLLDBTraceStart +/// \{ +bool TraceStartRequest::IsProcessTracing() const { return !(bool)tids; } + +bool fromJSON(const json::Value &value, TraceStartRequest &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type) && o.map("tids", packet.tids); +} + +json::Value toJSON(const TraceStartRequest &packet) { + return json::Value(Object{{"tids", packet.tids}, {"type", packet.type}}); +} +/// \} + +/// jLLDBTraceStop +/// \{ +TraceStopRequest::TraceStopRequest(llvm::StringRef type, + const std::vector<lldb::tid_t> &tids_) + : type(type) { + tids.emplace(); + for (lldb::tid_t tid : tids_) + tids->push_back(tid); +} + +bool TraceStopRequest::IsProcessTracing() const { return !(bool)tids; } + +bool fromJSON(const json::Value &value, TraceStopRequest &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type) && o.map("tids", packet.tids); +} + +json::Value toJSON(const TraceStopRequest &packet) { + return json::Value(Object{{"type", packet.type}, {"tids", packet.tids}}); +} +/// \} + +/// jLLDBTraceGetState +/// \{ +bool fromJSON(const json::Value &value, TraceGetStateRequest &packet, + Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type); +} + +json::Value toJSON(const TraceGetStateRequest &packet) { + return json::Value(Object{{"type", packet.type}}); +} + +bool fromJSON(const json::Value &value, TraceBinaryData &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("kind", packet.kind) && o.map("size", packet.size); +} + +json::Value toJSON(const TraceBinaryData &packet) { + return json::Value(Object{{"kind", packet.kind}, {"size", packet.size}}); +} + +bool fromJSON(const json::Value &value, TraceThreadState &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("tid", packet.tid) && + o.map("binaryData", packet.binary_data); +} + +json::Value toJSON(const TraceThreadState &packet) { + return json::Value( + Object{{"tid", packet.tid}, {"binaryData", packet.binary_data}}); +} + +bool fromJSON(const json::Value &value, TraceGetStateResponse &packet, + Path path) { + ObjectMapper o(value, path); + return o && o.map("tracedThreads", packet.traced_threads) && + o.map("processBinaryData", packet.process_binary_data) && + o.map("cpus", packet.cpus) && o.map("warnings", packet.warnings); +} + +json::Value toJSON(const TraceGetStateResponse &packet) { + return json::Value(Object{{"tracedThreads", packet.traced_threads}, + {"processBinaryData", packet.process_binary_data}, + {"cpus", packet.cpus}, + {"warnings", packet.warnings}}); +} + +void TraceGetStateResponse::AddWarning(StringRef warning) { + if (!warnings) + warnings.emplace(); + warnings->push_back(warning.data()); +} + +bool fromJSON(const json::Value &value, TraceCpuState &packet, + json::Path path) { + ObjectMapper o(value, path); + uint64_t cpu_id; + if (!(o && o.map("id", cpu_id) && o.map("binaryData", packet.binary_data))) + return false; + packet.id = static_cast<lldb::cpu_id_t>(cpu_id); + return true; +} + +json::Value toJSON(const TraceCpuState &packet) { + return json::Value( + Object{{"id", packet.id}, {"binaryData", packet.binary_data}}); +} +/// \} + +/// jLLDBTraceGetBinaryData +/// \{ +json::Value toJSON(const TraceGetBinaryDataRequest &packet) { + return json::Value(Object{{"type", packet.type}, + {"kind", packet.kind}, + {"tid", packet.tid}, + {"cpuId", packet.cpu_id}}); +} + +bool fromJSON(const json::Value &value, TraceGetBinaryDataRequest &packet, + Path path) { + ObjectMapper o(value, path); + std::optional<uint64_t> cpu_id; + if (!(o && o.map("type", packet.type) && o.map("kind", packet.kind) && + o.map("tid", packet.tid) && o.map("cpuId", cpu_id))) + return false; + + if (cpu_id) + packet.cpu_id = static_cast<lldb::cpu_id_t>(*cpu_id); + return true; +} +/// \} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/contrib/llvm-project/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp new file mode 100644 index 000000000000..20e16b8ed3a6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -0,0 +1,125 @@ +//===-- TraceIntelPTGDBRemotePackets.cpp ------------------------*- C++ -*-===// +// +// 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/Utility/TraceIntelPTGDBRemotePackets.h" + +using namespace llvm; +using namespace llvm::json; + +namespace lldb_private { + +const char *IntelPTDataKinds::kProcFsCpuInfo = "procfsCpuInfo"; +const char *IntelPTDataKinds::kIptTrace = "iptTrace"; +const char *IntelPTDataKinds::kPerfContextSwitchTrace = + "perfContextSwitchTrace"; + +bool TraceIntelPTStartRequest::IsPerCpuTracing() const { + return per_cpu_tracing.value_or(false); +} + +json::Value toJSON(const JSONUINT64 &uint64, bool hex) { + if (hex) + return json::Value(formatv("{0:x+}", uint64.value)); + else + return json::Value(formatv("{0}", uint64.value)); +} + +bool fromJSON(const json::Value &value, JSONUINT64 &uint64, Path path) { + if (std::optional<uint64_t> val = value.getAsUINT64()) { + uint64.value = *val; + return true; + } else if (std::optional<StringRef> val = value.getAsString()) { + if (!val->getAsInteger(/*radix=*/0, uint64.value)) + return true; + path.report("invalid string number"); + } + path.report("invalid number or string number"); + return false; +} + +bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet, + Path path) { + ObjectMapper o(value, path); + if (!(o && fromJSON(value, (TraceStartRequest &)packet, path) && + o.map("enableTsc", packet.enable_tsc) && + o.map("psbPeriod", packet.psb_period) && + o.map("iptTraceSize", packet.ipt_trace_size))) + return false; + + if (packet.IsProcessTracing()) { + if (!o.map("processBufferSizeLimit", packet.process_buffer_size_limit) || + !o.map("perCpuTracing", packet.per_cpu_tracing) || + !o.map("disableCgroupTracing", packet.disable_cgroup_filtering)) + return false; + } + return true; +} + +json::Value toJSON(const TraceIntelPTStartRequest &packet) { + json::Value base = toJSON((const TraceStartRequest &)packet); + json::Object &obj = *base.getAsObject(); + obj.try_emplace("iptTraceSize", packet.ipt_trace_size); + obj.try_emplace("processBufferSizeLimit", packet.process_buffer_size_limit); + obj.try_emplace("psbPeriod", packet.psb_period); + obj.try_emplace("enableTsc", packet.enable_tsc); + obj.try_emplace("perCpuTracing", packet.per_cpu_tracing); + obj.try_emplace("disableCgroupTracing", packet.disable_cgroup_filtering); + return base; +} + +uint64_t LinuxPerfZeroTscConversion::ToNanos(uint64_t tsc) const { + uint64_t quot = tsc >> time_shift; + uint64_t rem_flag = (((uint64_t)1 << time_shift) - 1); + uint64_t rem = tsc & rem_flag; + return time_zero.value + quot * time_mult + ((rem * time_mult) >> time_shift); +} + +uint64_t LinuxPerfZeroTscConversion::ToTSC(uint64_t nanos) const { + uint64_t time = nanos - time_zero.value; + uint64_t quot = time / time_mult; + uint64_t rem = time % time_mult; + return (quot << time_shift) + (rem << time_shift) / time_mult; +} + +json::Value toJSON(const LinuxPerfZeroTscConversion &packet) { + return json::Value(json::Object{ + {"timeMult", packet.time_mult}, + {"timeShift", packet.time_shift}, + {"timeZero", toJSON(packet.time_zero, /*hex=*/false)}, + }); +} + +bool fromJSON(const json::Value &value, LinuxPerfZeroTscConversion &packet, + json::Path path) { + ObjectMapper o(value, path); + uint64_t time_mult, time_shift; + if (!(o && o.map("timeMult", time_mult) && o.map("timeShift", time_shift) && + o.map("timeZero", packet.time_zero))) + return false; + packet.time_mult = time_mult; + packet.time_shift = time_shift; + return true; +} + +bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, + json::Path path) { + ObjectMapper o(value, path); + return o && fromJSON(value, (TraceGetStateResponse &)packet, path) && + o.map("tscPerfZeroConversion", packet.tsc_perf_zero_conversion) && + o.map("usingCgroupFiltering", packet.using_cgroup_filtering); +} + +json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { + json::Value base = toJSON((const TraceGetStateResponse &)packet); + json::Object &obj = *base.getAsObject(); + obj.insert({"tscPerfZeroConversion", packet.tsc_perf_zero_conversion}); + obj.insert({"usingCgroupFiltering", packet.using_cgroup_filtering}); + return base; +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Utility/UUID.cpp b/contrib/llvm-project/lldb/source/Utility/UUID.cpp new file mode 100644 index 000000000000..57e3a39d1f8e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/UUID.cpp @@ -0,0 +1,113 @@ +//===-- UUID.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/Utility/UUID.h" + +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" + +#include <cctype> +#include <cstdio> +#include <cstring> + +using namespace lldb_private; + +// Whether to put a separator after count uuid bytes. +// For the first 16 bytes we follow the traditional UUID format. After that, we +// simply put a dash after every 6 bytes. +static inline bool separate(size_t count) { + if (count >= 10) + return (count - 10) % 6 == 0; + + switch (count) { + case 4: + case 6: + case 8: + return true; + default: + return false; + } +} + +UUID::UUID(UUID::CvRecordPdb70 debug_info) { + llvm::sys::swapByteOrder(debug_info.Uuid.Data1); + llvm::sys::swapByteOrder(debug_info.Uuid.Data2); + llvm::sys::swapByteOrder(debug_info.Uuid.Data3); + llvm::sys::swapByteOrder(debug_info.Age); + if (debug_info.Age) + *this = UUID(&debug_info, sizeof(debug_info)); + else + *this = UUID(&debug_info.Uuid, sizeof(debug_info.Uuid)); +} + +std::string UUID::GetAsString(llvm::StringRef separator) const { + std::string result; + llvm::raw_string_ostream os(result); + + for (auto B : llvm::enumerate(GetBytes())) { + if (separate(B.index())) + os << separator; + + os << llvm::format_hex_no_prefix(B.value(), 2, true); + } + os.flush(); + + return result; +} + +void UUID::Dump(Stream &s) const { s.PutCString(GetAsString()); } + +static inline int xdigit_to_int(char ch) { + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + return ch - '0'; +} + +llvm::StringRef +UUID::DecodeUUIDBytesFromString(llvm::StringRef p, + llvm::SmallVectorImpl<uint8_t> &uuid_bytes) { + uuid_bytes.clear(); + while (p.size() >= 2) { + if (isxdigit(p[0]) && isxdigit(p[1])) { + int hi_nibble = xdigit_to_int(p[0]); + int lo_nibble = xdigit_to_int(p[1]); + // Translate the two hex nibble characters into a byte + uuid_bytes.push_back((hi_nibble << 4) + lo_nibble); + + // Skip both hex digits + p = p.drop_front(2); + } else if (p.front() == '-') { + // Skip dashes + p = p.drop_front(); + } else { + // UUID values can only consist of hex characters and '-' chars + break; + } + } + return p; +} + +bool UUID::SetFromStringRef(llvm::StringRef str) { + llvm::StringRef p = str; + + // Skip leading whitespace characters + p = p.ltrim(); + + llvm::SmallVector<uint8_t, 20> bytes; + llvm::StringRef rest = UUID::DecodeUUIDBytesFromString(p, bytes); + + // Return false if we could not consume the entire string or if the parsed + // UUID is empty. + if (!rest.empty() || bytes.empty()) + return false; + + *this = UUID(bytes); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Utility/UnimplementedError.cpp b/contrib/llvm-project/lldb/source/Utility/UnimplementedError.cpp new file mode 100644 index 000000000000..034ad5b17b64 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/UnimplementedError.cpp @@ -0,0 +1,11 @@ +//===-- UnimplementedError.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/Utility/UnimplementedError.h" + +char lldb_private::UnimplementedError::ID; diff --git a/contrib/llvm-project/lldb/source/Utility/UriParser.cpp b/contrib/llvm-project/lldb/source/Utility/UriParser.cpp new file mode 100644 index 000000000000..1932e11acb4c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/UriParser.cpp @@ -0,0 +1,74 @@ +//===-- UriParser.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/Utility/UriParser.h" +#include "llvm/Support/raw_ostream.h" + +#include <string> + +#include <cstdint> +#include <optional> +#include <tuple> + +using namespace lldb_private; + +llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS, + const URI &U) { + OS << U.scheme << "://[" << U.hostname << ']'; + if (U.port) + OS << ':' << *U.port; + return OS << U.path; +} + +std::optional<URI> URI::Parse(llvm::StringRef uri) { + URI ret; + + const llvm::StringRef kSchemeSep("://"); + auto pos = uri.find(kSchemeSep); + if (pos == std::string::npos) + return std::nullopt; + + // Extract path. + ret.scheme = uri.substr(0, pos); + auto host_pos = pos + kSchemeSep.size(); + auto path_pos = uri.find('/', host_pos); + if (path_pos != std::string::npos) + ret.path = uri.substr(path_pos); + else + ret.path = "/"; + + auto host_port = uri.substr( + host_pos, + ((path_pos != std::string::npos) ? path_pos : uri.size()) - host_pos); + + // Extract hostname + if (host_port.starts_with('[')) { + // hostname is enclosed with square brackets. + pos = host_port.rfind(']'); + if (pos == std::string::npos) + return std::nullopt; + + ret.hostname = host_port.substr(1, pos - 1); + host_port = host_port.drop_front(pos + 1); + if (!host_port.empty() && !host_port.consume_front(":")) + return std::nullopt; + } else { + std::tie(ret.hostname, host_port) = host_port.split(':'); + } + + // Extract port + if (!host_port.empty()) { + uint16_t port_value = 0; + if (host_port.getAsInteger(0, port_value)) + return std::nullopt; + ret.port = port_value; + } else + ret.port = std::nullopt; + + return ret; +} diff --git a/contrib/llvm-project/lldb/source/Utility/UserID.cpp b/contrib/llvm-project/lldb/source/Utility/UserID.cpp new file mode 100644 index 000000000000..5bae91a91288 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/UserID.cpp @@ -0,0 +1,20 @@ +//===-- UserID.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/Utility/UserID.h" +#include "lldb/Utility/Stream.h" + +#include <cinttypes> + +using namespace lldb; +using namespace lldb_private; + +Stream &lldb_private::operator<<(Stream &strm, const UserID &uid) { + strm.Printf("{0x%8.8" PRIx64 "}", uid.GetID()); + return strm; +} diff --git a/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp b/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp new file mode 100644 index 000000000000..83e033f32310 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp @@ -0,0 +1,45 @@ +//===-- UserIDResolver.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/Utility/UserIDResolver.h" +#include "llvm/Support/ManagedStatic.h" +#include <optional> + +using namespace lldb_private; + +UserIDResolver::~UserIDResolver() = default; + +std::optional<llvm::StringRef> UserIDResolver::Get( + id_t id, Map &cache, + std::optional<std::string> (UserIDResolver::*do_get)(id_t)) { + + std::lock_guard<std::mutex> guard(m_mutex); + auto iter_bool = cache.try_emplace(id, std::nullopt); + if (iter_bool.second) + iter_bool.first->second = (this->*do_get)(id); + if (iter_bool.first->second) + return llvm::StringRef(*iter_bool.first->second); + return std::nullopt; +} + +namespace { +class NoopResolver : public UserIDResolver { +protected: + std::optional<std::string> DoGetUserName(id_t uid) override { + return std::nullopt; + } + + std::optional<std::string> DoGetGroupName(id_t gid) override { + return std::nullopt; + } +}; +} // namespace + +static llvm::ManagedStatic<NoopResolver> g_noop_resolver; + +UserIDResolver &UserIDResolver::GetNoopResolver() { return *g_noop_resolver; } diff --git a/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp b/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp new file mode 100644 index 000000000000..5d361edf06e6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp @@ -0,0 +1,55 @@ +//===-- VASprintf.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/Utility/VASPrintf.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include <cassert> +#include <cstdarg> +#include <cstdio> + +bool lldb_private::VASprintf(llvm::SmallVectorImpl<char> &buf, const char *fmt, + va_list args) { + llvm::SmallString<16> error("<Encoding error>"); + bool result = true; + + // Copy in case our first call to vsnprintf doesn't fit into our buffer + va_list copy_args; + va_copy(copy_args, args); + + buf.resize(buf.capacity()); + // Write up to `capacity` bytes, ignoring the current size. + int length = ::vsnprintf(buf.data(), buf.size(), fmt, args); + if (length < 0) { + buf = error; + result = false; + goto finish; + } + + if (size_t(length) >= buf.size()) { + // The error formatted string didn't fit into our buffer, resize it to the + // exact needed size, and retry + buf.resize(length + 1); + length = ::vsnprintf(buf.data(), buf.size(), fmt, copy_args); + if (length < 0) { + buf = error; + result = false; + goto finish; + } + assert(size_t(length) < buf.size()); + } + buf.resize(length); + +finish: + va_end(args); + va_end(copy_args); + return result; +} diff --git a/contrib/llvm-project/lldb/source/Utility/VMRange.cpp b/contrib/llvm-project/lldb/source/Utility/VMRange.cpp new file mode 100644 index 000000000000..ddd2a67c29b2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/VMRange.cpp @@ -0,0 +1,69 @@ +//===-- VMRange.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/Utility/VMRange.h" + +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-types.h" + +#include <algorithm> +#include <iterator> +#include <vector> + +#include <cstddef> +#include <cstdint> + +using namespace lldb; +using namespace lldb_private; + +bool VMRange::ContainsValue(const VMRange::collection &coll, + lldb::addr_t value) { + return llvm::any_of(coll, + [&](const VMRange &r) { return r.Contains(value); }); +} + +bool VMRange::ContainsRange(const VMRange::collection &coll, + const VMRange &range) { + return llvm::any_of(coll, + [&](const VMRange &r) { return r.Contains(range); }); +} + +void VMRange::Dump(llvm::raw_ostream &s, lldb::addr_t offset, + uint32_t addr_width) const { + DumpAddressRange(s, offset + GetBaseAddress(), offset + GetEndAddress(), + addr_width); +} + +bool lldb_private::operator==(const VMRange &lhs, const VMRange &rhs) { + return lhs.GetBaseAddress() == rhs.GetBaseAddress() && + lhs.GetEndAddress() == rhs.GetEndAddress(); +} + +bool lldb_private::operator!=(const VMRange &lhs, const VMRange &rhs) { + return !(lhs == rhs); +} + +bool lldb_private::operator<(const VMRange &lhs, const VMRange &rhs) { + if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() < rhs.GetEndAddress(); +} + +bool lldb_private::operator<=(const VMRange &lhs, const VMRange &rhs) { + return !(lhs > rhs); +} + +bool lldb_private::operator>(const VMRange &lhs, const VMRange &rhs) { + return rhs < lhs; +} + +bool lldb_private::operator>=(const VMRange &lhs, const VMRange &rhs) { + return !(lhs < rhs); +} diff --git a/contrib/llvm-project/lldb/source/Utility/XcodeSDK.cpp b/contrib/llvm-project/lldb/source/Utility/XcodeSDK.cpp new file mode 100644 index 000000000000..712d611db28f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/XcodeSDK.cpp @@ -0,0 +1,329 @@ +//===-- XcodeSDK.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/Utility/XcodeSDK.h" +#include "lldb/Utility/FileSpec.h" + +#include "lldb/lldb-types.h" + +#include "llvm/TargetParser/Triple.h" + +#include <string> + +using namespace lldb; +using namespace lldb_private; + +static llvm::StringRef GetName(XcodeSDK::Type type) { + switch (type) { + case XcodeSDK::MacOSX: + return "MacOSX"; + case XcodeSDK::iPhoneSimulator: + return "iPhoneSimulator"; + case XcodeSDK::iPhoneOS: + return "iPhoneOS"; + case XcodeSDK::AppleTVSimulator: + return "AppleTVSimulator"; + case XcodeSDK::AppleTVOS: + return "AppleTVOS"; + case XcodeSDK::WatchSimulator: + return "WatchSimulator"; + case XcodeSDK::watchOS: + return "WatchOS"; + case XcodeSDK::XRSimulator: + return "XRSimulator"; + case XcodeSDK::XROS: + return "XROS"; + case XcodeSDK::bridgeOS: + return "bridgeOS"; + case XcodeSDK::Linux: + return "Linux"; + case XcodeSDK::unknown: + return {}; + } + llvm_unreachable("Unhandled sdk type!"); +} + +XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) { + if (!m_name.empty()) { + if (!info.version.empty()) + m_name += info.version.getAsString(); + if (info.internal) + m_name += ".Internal"; + m_name += ".sdk"; + } +} + +XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) = default; + +bool XcodeSDK::operator==(const XcodeSDK &other) const { + return m_name == other.m_name; +} + +static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) { + if (name.consume_front("MacOSX")) + return XcodeSDK::MacOSX; + if (name.consume_front("iPhoneSimulator")) + return XcodeSDK::iPhoneSimulator; + if (name.consume_front("iPhoneOS")) + return XcodeSDK::iPhoneOS; + if (name.consume_front("AppleTVSimulator")) + return XcodeSDK::AppleTVSimulator; + if (name.consume_front("AppleTVOS")) + return XcodeSDK::AppleTVOS; + if (name.consume_front("WatchSimulator")) + return XcodeSDK::WatchSimulator; + if (name.consume_front("WatchOS")) + return XcodeSDK::watchOS; + if (name.consume_front("XRSimulator")) + return XcodeSDK::XRSimulator; + if (name.consume_front("XROS")) + return XcodeSDK::XROS; + if (name.consume_front("bridgeOS")) + return XcodeSDK::bridgeOS; + if (name.consume_front("Linux")) + return XcodeSDK::Linux; + static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1, + "New SDK type was added, update this list!"); + return XcodeSDK::unknown; +} + +static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) { + unsigned i = 0; + while (i < name.size() && name[i] >= '0' && name[i] <= '9') + ++i; + if (i == name.size() || name[i++] != '.') + return {}; + while (i < name.size() && name[i] >= '0' && name[i] <= '9') + ++i; + if (i == name.size() || name[i++] != '.') + return {}; + + llvm::VersionTuple version; + version.tryParse(name.slice(0, i - 1)); + name = name.drop_front(i); + return version; +} + +static bool ParseAppleInternalSDK(llvm::StringRef &name) { + return name.consume_front("Internal.") || name.consume_front(".Internal."); +} + +XcodeSDK::Info XcodeSDK::Parse() const { + XcodeSDK::Info info; + llvm::StringRef input(m_name); + info.type = ParseSDKName(input); + info.version = ParseSDKVersion(input); + info.internal = ParseAppleInternalSDK(input); + return info; +} + +bool XcodeSDK::IsAppleInternalSDK() const { + llvm::StringRef input(m_name); + ParseSDKName(input); + ParseSDKVersion(input); + return ParseAppleInternalSDK(input); +} + +llvm::VersionTuple XcodeSDK::GetVersion() const { + llvm::StringRef input(m_name); + ParseSDKName(input); + return ParseSDKVersion(input); +} + +XcodeSDK::Type XcodeSDK::GetType() const { + llvm::StringRef input(m_name); + return ParseSDKName(input); +} + +llvm::StringRef XcodeSDK::GetString() const { return m_name; } + +bool XcodeSDK::Info::operator<(const Info &other) const { + return std::tie(type, version, internal) < + std::tie(other.type, other.version, other.internal); +} + +bool XcodeSDK::Info::operator==(const Info &other) const { + return std::tie(type, version, internal) == + std::tie(other.type, other.version, other.internal); +} + +void XcodeSDK::Merge(const XcodeSDK &other) { + // The "bigger" SDK always wins. + auto l = Parse(); + auto r = other.Parse(); + if (l < r) + *this = other; + else { + // The Internal flag always wins. + if (llvm::StringRef(m_name).ends_with(".sdk")) + if (!l.internal && r.internal) + m_name = + m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk"); + } +} + +std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) { + std::string name; + switch (info.type) { + case MacOSX: + name = "macosx"; + break; + case iPhoneSimulator: + name = "iphonesimulator"; + break; + case iPhoneOS: + name = "iphoneos"; + break; + case AppleTVSimulator: + name = "appletvsimulator"; + break; + case AppleTVOS: + name = "appletvos"; + break; + case WatchSimulator: + name = "watchsimulator"; + break; + case watchOS: + name = "watchos"; + break; + case XRSimulator: + name = "xrsimulator"; + break; + case XROS: + name = "xros"; + break; + case bridgeOS: + name = "bridgeos"; + break; + case Linux: + name = "linux"; + break; + case unknown: + return {}; + } + if (!info.version.empty()) + name += info.version.getAsString(); + if (info.internal) + name += ".internal"; + return name; +} + +bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, + llvm::VersionTuple version) { + switch (sdk_type) { + case Type::MacOSX: + return version >= llvm::VersionTuple(10, 10); + case Type::iPhoneOS: + case Type::iPhoneSimulator: + case Type::AppleTVOS: + case Type::AppleTVSimulator: + return version >= llvm::VersionTuple(8); + case Type::watchOS: + case Type::WatchSimulator: + return version >= llvm::VersionTuple(6); + case Type::XROS: + case Type::XRSimulator: + return true; + default: + return false; + } + + return false; +} + +bool XcodeSDK::SupportsSwift() const { + XcodeSDK::Info info = Parse(); + switch (info.type) { + case Type::MacOSX: + return info.version.empty() || info.version >= llvm::VersionTuple(10, 10); + case Type::iPhoneOS: + case Type::iPhoneSimulator: + return info.version.empty() || info.version >= llvm::VersionTuple(8); + case Type::AppleTVSimulator: + case Type::AppleTVOS: + return info.version.empty() || info.version >= llvm::VersionTuple(9); + case Type::WatchSimulator: + case Type::watchOS: + return info.version.empty() || info.version >= llvm::VersionTuple(2); + case Type::XROS: + case Type::XRSimulator: + case Type::Linux: + return true; + default: + return false; + } +} + +bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, + const FileSpec &sdk_path) { + ConstString last_path_component = sdk_path.GetFilename(); + + if (!last_path_component) + return false; + + XcodeSDK sdk(last_path_component.GetStringRef().str()); + if (sdk.GetType() != desired_type) + return false; + return SDKSupportsModules(sdk.GetType(), sdk.GetVersion()); +} + +XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) { + using namespace llvm; + switch (triple.getOS()) { + case Triple::MacOSX: + case Triple::Darwin: + return XcodeSDK::MacOSX; + case Triple::IOS: + switch (triple.getEnvironment()) { + case Triple::MacABI: + return XcodeSDK::MacOSX; + case Triple::Simulator: + return XcodeSDK::iPhoneSimulator; + default: + return XcodeSDK::iPhoneOS; + } + case Triple::TvOS: + if (triple.getEnvironment() == Triple::Simulator) + return XcodeSDK::AppleTVSimulator; + return XcodeSDK::AppleTVOS; + case Triple::WatchOS: + if (triple.getEnvironment() == Triple::Simulator) + return XcodeSDK::WatchSimulator; + return XcodeSDK::watchOS; + case Triple::XROS: + if (triple.getEnvironment() == Triple::Simulator) + return XcodeSDK::XRSimulator; + return XcodeSDK::XROS; + case Triple::Linux: + return XcodeSDK::Linux; + default: + return XcodeSDK::unknown; + } +} + +std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) { + auto begin = llvm::sys::path::begin(path); + auto end = llvm::sys::path::end(path); + + // Iterate over the path components until we find something that ends with + // .app. If the next component is Contents then we've found the Contents + // directory. + for (auto it = begin; it != end; ++it) { + if (it->ends_with(".app")) { + auto next = it; + if (++next != end && *next == "Contents") { + llvm::SmallString<128> buffer; + llvm::sys::path::append(buffer, begin, ++next, + llvm::sys::path::Style::posix); + return buffer.str().str(); + } + } + } + + return {}; +} diff --git a/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp b/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp new file mode 100644 index 000000000000..b8ed956cbfcb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp @@ -0,0 +1,180 @@ +//===-- ZipFile.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/Utility/ZipFile.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/FileSpec.h" +#include "llvm/Support/Endian.h" + +using namespace lldb_private; +using namespace llvm::support; + +namespace { + +// Zip headers. +// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + +// The end of central directory record. +struct EocdRecord { + static constexpr char kSignature[] = {0x50, 0x4b, 0x05, 0x06}; + char signature[sizeof(kSignature)]; + unaligned_uint16_t disks; + unaligned_uint16_t cd_start_disk; + unaligned_uint16_t cds_on_this_disk; + unaligned_uint16_t cd_records; + unaligned_uint32_t cd_size; + unaligned_uint32_t cd_offset; + unaligned_uint16_t comment_length; +}; + +// Logical find limit for the end of central directory record. +const size_t kEocdRecordFindLimit = + sizeof(EocdRecord) + + std::numeric_limits<decltype(EocdRecord::comment_length)>::max(); + +// Central directory record. +struct CdRecord { + static constexpr char kSignature[] = {0x50, 0x4b, 0x01, 0x02}; + char signature[sizeof(kSignature)]; + unaligned_uint16_t version_made_by; + unaligned_uint16_t version_needed_to_extract; + unaligned_uint16_t general_purpose_bit_flag; + unaligned_uint16_t compression_method; + unaligned_uint16_t last_modification_time; + unaligned_uint16_t last_modification_date; + unaligned_uint32_t crc32; + unaligned_uint32_t compressed_size; + unaligned_uint32_t uncompressed_size; + unaligned_uint16_t file_name_length; + unaligned_uint16_t extra_field_length; + unaligned_uint16_t comment_length; + unaligned_uint16_t file_start_disk; + unaligned_uint16_t internal_file_attributes; + unaligned_uint32_t external_file_attributes; + unaligned_uint32_t local_file_header_offset; +}; +// Immediately after CdRecord, +// - file name (file_name_length) +// - extra field (extra_field_length) +// - comment (comment_length) + +// Local file header. +struct LocalFileHeader { + static constexpr char kSignature[] = {0x50, 0x4b, 0x03, 0x04}; + char signature[sizeof(kSignature)]; + unaligned_uint16_t version_needed_to_extract; + unaligned_uint16_t general_purpose_bit_flag; + unaligned_uint16_t compression_method; + unaligned_uint16_t last_modification_time; + unaligned_uint16_t last_modification_date; + unaligned_uint32_t crc32; + unaligned_uint32_t compressed_size; + unaligned_uint32_t uncompressed_size; + unaligned_uint16_t file_name_length; + unaligned_uint16_t extra_field_length; +}; +// Immediately after LocalFileHeader, +// - file name (file_name_length) +// - extra field (extra_field_length) +// - file data (should be compressed_size == uncompressed_size, page aligned) + +const EocdRecord *FindEocdRecord(lldb::DataBufferSP zip_data) { + // Find backward the end of central directory record from the end of the zip + // file to the find limit. + const uint8_t *zip_data_end = zip_data->GetBytes() + zip_data->GetByteSize(); + const uint8_t *find_limit = zip_data_end - kEocdRecordFindLimit; + const uint8_t *p = zip_data_end - sizeof(EocdRecord); + for (; p >= zip_data->GetBytes() && p >= find_limit; p--) { + auto eocd = reinterpret_cast<const EocdRecord *>(p); + if (::memcmp(eocd->signature, EocdRecord::kSignature, + sizeof(EocdRecord::kSignature)) == 0) { + // Found the end of central directory. Sanity check the values. + if (eocd->cd_records * sizeof(CdRecord) > eocd->cd_size || + zip_data->GetBytes() + eocd->cd_offset + eocd->cd_size > p) + return nullptr; + + // This is a valid end of central directory record. + return eocd; + } + } + return nullptr; +} + +bool GetFile(lldb::DataBufferSP zip_data, uint32_t local_file_header_offset, + lldb::offset_t &file_offset, lldb::offset_t &file_size) { + auto local_file_header = reinterpret_cast<const LocalFileHeader *>( + zip_data->GetBytes() + local_file_header_offset); + // The signature should match. + if (::memcmp(local_file_header->signature, LocalFileHeader::kSignature, + sizeof(LocalFileHeader::kSignature)) != 0) + return false; + + auto file_data = reinterpret_cast<const uint8_t *>(local_file_header + 1) + + local_file_header->file_name_length + + local_file_header->extra_field_length; + // File should be uncompressed. + if (local_file_header->compressed_size != + local_file_header->uncompressed_size) + return false; + + // This file is valid. Return the file offset and size. + file_offset = file_data - zip_data->GetBytes(); + file_size = local_file_header->uncompressed_size; + return true; +} + +bool FindFile(lldb::DataBufferSP zip_data, const EocdRecord *eocd, + const llvm::StringRef file_path, lldb::offset_t &file_offset, + lldb::offset_t &file_size) { + // Find the file from the central directory records. + auto cd = reinterpret_cast<const CdRecord *>(zip_data->GetBytes() + + eocd->cd_offset); + size_t cd_records = eocd->cd_records; + for (size_t i = 0; i < cd_records; i++) { + // The signature should match. + if (::memcmp(cd->signature, CdRecord::kSignature, + sizeof(CdRecord::kSignature)) != 0) + return false; + + // Sanity check the file name values. + auto file_name = reinterpret_cast<const char *>(cd + 1); + size_t file_name_length = cd->file_name_length; + if (file_name + file_name_length >= reinterpret_cast<const char *>(eocd) || + file_name_length == 0) + return false; + + // Compare the file name. + if (file_path == llvm::StringRef(file_name, file_name_length)) { + // Found the file. + return GetFile(zip_data, cd->local_file_header_offset, file_offset, + file_size); + } else { + // Skip to the next central directory record. + cd = reinterpret_cast<const CdRecord *>( + reinterpret_cast<const char *>(cd) + sizeof(CdRecord) + + cd->file_name_length + cd->extra_field_length + cd->comment_length); + // Sanity check the pointer. + if (reinterpret_cast<const char *>(cd) >= + reinterpret_cast<const char *>(eocd)) + return false; + } + } + + return false; +} + +} // end anonymous namespace + +bool ZipFile::Find(lldb::DataBufferSP zip_data, const llvm::StringRef file_path, + lldb::offset_t &file_offset, lldb::offset_t &file_size) { + const EocdRecord *eocd = FindEocdRecord(zip_data); + if (!eocd) + return false; + + return FindFile(zip_data, eocd, file_path, file_offset, file_size); +} |