diff options
Diffstat (limited to 'lldb/source/Utility')
55 files changed, 15500 insertions, 0 deletions
diff --git a/lldb/source/Utility/ARM64_DWARF_Registers.h b/lldb/source/Utility/ARM64_DWARF_Registers.h new file mode 100644 index 0000000000000..64f69d6435654 --- /dev/null +++ b/lldb/source/Utility/ARM64_DWARF_Registers.h @@ -0,0 +1,95 @@ +//===-- 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 utility_ARM64_DWARF_Registers_h_ +#define 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-63 reserved + +  // 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 + +  // 96-127 reserved +}; + +} // namespace arm64_dwarf + +#endif // utility_ARM64_DWARF_Registers_h_ diff --git a/lldb/source/Utility/ARM64_ehframe_Registers.h b/lldb/source/Utility/ARM64_ehframe_Registers.h new file mode 100644 index 0000000000000..9b5cd931bf28b --- /dev/null +++ b/lldb/source/Utility/ARM64_ehframe_Registers.h @@ -0,0 +1,91 @@ +//===-- 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 utility_ARM64_ehframe_Registers_h_ +#define 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 +}; + +enum { +  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 // 95 +}; +} + +#endif // utility_ARM64_ehframe_Registers_h_ diff --git a/lldb/source/Utility/ARM_DWARF_Registers.h b/lldb/source/Utility/ARM_DWARF_Registers.h new file mode 100644 index 0000000000000..e33210dfbfbd4 --- /dev/null +++ b/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 utility_ARM_DWARF_Registers_h_ +#define 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 // utility_ARM_DWARF_Registers_h_ diff --git a/lldb/source/Utility/ARM_ehframe_Registers.h b/lldb/source/Utility/ARM_ehframe_Registers.h new file mode 100644 index 0000000000000..1816b1d974975 --- /dev/null +++ b/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 utility_ARM_ehframe_Registers_h_ +#define 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 // utility_ARM_ehframe_Registers_h_ diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp new file mode 100644 index 0000000000000..40cc4a092b0d8 --- /dev/null +++ b/lldb/source/Utility/ArchSpec.cpp @@ -0,0 +1,1471 @@ +//===-- ArchSpec.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/ArchSpec.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#include "lldb/lldb-defines.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Host.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_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, 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"}, + +    {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, 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"} +}; + +// 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 (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) +    list.AppendString(g_core_definitions[i].name); +} + +void ArchSpec::AutoComplete(CompletionRequest &request) { +  for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) +    request.TryCompleteCurrentArg(g_core_definitions[i].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 + +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, 0, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv4, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv5t, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_xscale, llvm::MachO::CPU_TYPE_ARM, 8, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv7, llvm::MachO::CPU_TYPE_ARM, 9, 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, 11, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_armv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 1, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 0, 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, 0, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv7, llvm::MachO::CPU_TYPE_ARM, 9, 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, 11, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_thumbv7em, llvm::MachO::CPU_TYPE_ARM, 16, 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, 0, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc601, llvm::MachO::CPU_TYPE_POWERPC, 1, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc602, llvm::MachO::CPU_TYPE_POWERPC, 2, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc603, llvm::MachO::CPU_TYPE_POWERPC, 3, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc603e, llvm::MachO::CPU_TYPE_POWERPC, 4, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc603ev, llvm::MachO::CPU_TYPE_POWERPC, 5, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc604, llvm::MachO::CPU_TYPE_POWERPC, 6, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc604e, llvm::MachO::CPU_TYPE_POWERPC, 7, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc620, llvm::MachO::CPU_TYPE_POWERPC, 8, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc750, llvm::MachO::CPU_TYPE_POWERPC, 9, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc7400, llvm::MachO::CPU_TYPE_POWERPC, 10, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc7450, llvm::MachO::CPU_TYPE_POWERPC, 11, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc_ppc970, llvm::MachO::CPU_TYPE_POWERPC, 100, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_ppc64_generic, llvm::MachO::CPU_TYPE_POWERPC64, 0, +     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, 3, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_x86_32_i486, llvm::MachO::CPU_TYPE_I386, 4, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_x86_32_i486sx, llvm::MachO::CPU_TYPE_I386, 0x84, +     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, 3, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 4, UINT32_MAX, +     SUBTYPE_MASK}, +    {ArchSpec::eCore_x86_64_x86_64h, llvm::MachO::CPU_TYPE_X86_64, 8, +     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}}; + +static const ArchDefinition g_macho_arch_def = { +    eArchTypeMachO, llvm::array_lengthof(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, LLDB_INVALID_CPUTYPE, +     0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le +    {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE, +     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_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 +}; + +static const ArchDefinition g_elf_arch_def = { +    eArchTypeELF, +    llvm::array_lengthof(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, +    llvm::array_lengthof(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 const size_t k_num_arch_definitions = +    llvm::array_lengthof(g_arch_definitions); + +//===----------------------------------------------------------------------===// +// Static helper functions. + +// Get the architecture definition for a given object type. +static const ArchDefinition *FindArchDefinition(ArchitectureType arch_type) { +  for (unsigned int i = 0; i < k_num_arch_definitions; ++i) { +    const ArchDefinition *def = g_arch_definitions[i]; +    if (def->type == arch_type) +      return def; +  } +  return nullptr; +} + +// Get an architecture definition by name. +static const CoreDefinition *FindCoreDefinition(llvm::StringRef name) { +  for (unsigned int i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { +    if (name.equals_lower(g_core_definitions[i].name)) +      return &g_core_definitions[i]; +  } +  return nullptr; +} + +static inline const CoreDefinition *FindCoreDefinition(ArchSpec::Core core) { +  if (core < llvm::array_lengthof(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() {} + +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; + +//===----------------------------------------------------------------------===// +// Assignment and initialization. + +const ArchSpec &ArchSpec::operator=(const ArchSpec &rhs) { +  if (this != &rhs) { +    m_triple = rhs.m_triple; +    m_core = rhs.m_core; +    m_byte_order = rhs.m_byte_order; +    m_distribution_id = rhs.m_distribution_id; +    m_flags = rhs.m_flags; +  } +  return *this; +} + +void ArchSpec::Clear() { +  m_triple = llvm::Triple(); +  m_core = kCore_invalid; +  m_byte_order = eByteOrderInvalid; +  m_distribution_id.Clear(); +  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(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; +    } +  } +  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; +} + +ConstString ArchSpec::GetDistributionId() const { +  return m_distribution_id; +} + +void ArchSpec::SetDistributionId(const char *distribution_id) { +  m_distribution_id.SetCString(distribution_id); +} + +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) { +  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(true); +  } +  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; +          } +        } 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET | LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_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; +} + +bool ArchSpec::IsExactMatch(const ArchSpec &rhs) const { +  return IsEqualTo(rhs, true); +} + +bool ArchSpec::IsCompatibleMatch(const ArchSpec &rhs) const { +  return IsEqualTo(rhs, false); +} + +static bool IsCompatibleEnvironment(llvm::Triple::EnvironmentType lhs, +                                    llvm::Triple::EnvironmentType rhs) { +  if (lhs == rhs) +    return true; + +  // 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::IsEqualTo(const ArchSpec &rhs, bool exact_match) const { +  // explicitly ignoring m_distribution_id in this method. + +  if (GetByteOrder() != rhs.GetByteOrder()) +    return false; + +  const ArchSpec::Core lhs_core = GetCore(); +  const ArchSpec::Core rhs_core = rhs.GetCore(); + +  const bool core_match = cores_match(lhs_core, rhs_core, true, exact_match); + +  if (core_match) { +    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(); +    if (lhs_triple_vendor != rhs_triple_vendor) { +      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::OSType lhs_triple_os = lhs_triple.getOS(); +    const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); +    if (lhs_triple_os != rhs_triple_os) { +      const bool rhs_os_specified = rhs.TripleOSWasSpecified(); +      const bool lhs_os_specified = TripleOSWasSpecified(); +      // Both architectures had the OS specified, so if they aren't equal then +      // we return false +      if (rhs_os_specified && lhs_os_specified) +        return false; + +      // Only fail if both os types are not unknown +      if (lhs_triple_os != llvm::Triple::UnknownOS && +          rhs_triple_os != llvm::Triple::UnknownOS) +        return false; +    } + +    const llvm::Triple::EnvironmentType lhs_triple_env = +        lhs_triple.getEnvironment(); +    const llvm::Triple::EnvironmentType rhs_triple_env = +        rhs_triple.getEnvironment(); + +    return IsCompatibleEnvironment(lhs_triple_env, rhs_triple_env); +  } +  return false; +} + +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; +    LLVM_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::eCore_arm_armv6m: +    if (!enforce_exact_match) { +      if (core2 == ArchSpec::eCore_arm_generic) +        return true; +      try_inverse = false; +      if (core2 == ArchSpec::eCore_arm_armv7) +        return true; +      if (core2 == ArchSpec::eCore_arm_armv6m) +        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; + +  case ArchSpec::eCore_arm_armv7f: +  case ArchSpec::eCore_arm_armv7k: +  case ArchSpec::eCore_arm_armv7s: +    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; +      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; +      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; +      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 { +  const auto &user_specified_triple = GetTriple(); + +  bool user_triple_fully_specified = false; + +  if ((user_specified_triple.getOS() != llvm::Triple::UnknownOS) || +      TripleOSWasSpecified()) { +    if ((user_specified_triple.getVendor() != llvm::Triple::UnknownVendor) || +        TripleVendorWasSpecified()) { +      const unsigned unspecified = 0; +      if (user_specified_triple.getOSMajorVersion() != unspecified) { +        user_triple_fully_specified = true; +      } +    } +  } + +  return user_triple_fully_specified; +} + +void ArchSpec::PiecewiseTripleCompare( +    const ArchSpec &other, bool &arch_different, bool &vendor_different, +    bool &os_different, bool &os_version_different, bool &env_different) const { +  const llvm::Triple &me(GetTriple()); +  const llvm::Triple &them(other.GetTriple()); + +  arch_different = (me.getArch() != them.getArch()); + +  vendor_different = (me.getVendor() != them.getVendor()); + +  os_different = (me.getOS() != them.getOS()); + +  os_version_different = (me.getOSMajorVersion() != them.getOSMajorVersion()); + +  env_different = (me.getEnvironment() != them.getEnvironment()); +} + +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; +    } +  } +  return false; +} + +void ArchSpec::DumpTriple(Stream &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.Printf("%s-%s-%s", arch_str.empty() ? "*" : arch_str.str().c_str(), +           vendor_str.empty() ? "*" : vendor_str.str().c_str(), +           os_str.empty() ? "*" : os_str.str().c_str()); + +  if (!environ_str.empty()) +    s.Printf("-%s", environ_str.str().c_str()); +} diff --git a/lldb/source/Utility/Args.cpp b/lldb/source/Utility/Args.cpp new file mode 100644 index 0000000000000..9fcc833ce432b --- /dev/null +++ b/lldb/source/Utility/Args.cpp @@ -0,0 +1,686 @@ +//===-- Args.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/Args.h" +#include "lldb/Utility/ConstString.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 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 a whitelisted 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 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 a whitelisted 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::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() {} + +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 += ' '; +    command += m_entries[i].ref(); +  } + +  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); +} + +size_t Args::GetArgumentCount() const { return m_entries.size(); } + +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::makeArrayRef(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::makeArrayRef(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); +} + +const char *Args::GetShellSafeArgument(const FileSpec &shell, +                                       const char *unsafe_arg, +                                       std::string &safe_arg) { +  struct ShellDescriptor { +    ConstString m_basename; +    const char *m_escapables; +  }; + +  static ShellDescriptor g_Shells[] = {{ConstString("bash"), " '\"<>()&"}, +                                       {ConstString("tcsh"), " '\"<>()&$"}, +                                       {ConstString("sh"), " '\"<>()&"}}; + +  // safe minimal set +  const char *escapables = " '\""; + +  if (auto basename = shell.GetFilename()) { +    for (const auto &Shell : g_Shells) { +      if (Shell.m_basename == basename) { +        escapables = Shell.m_escapables; +        break; +      } +    } +  } + +  safe_arg.assign(unsafe_arg); +  size_t prev_pos = 0; +  while (prev_pos < safe_arg.size()) { +    // Escape spaces and quotes +    size_t pos = safe_arg.find_first_of(escapables, prev_pos); +    if (pos != std::string::npos) { +      safe_arg.insert(pos, 1, '\\'); +      prev_pos = pos + 2; +    } else +      break; +  } +  return safe_arg.c_str(); +} + +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) +                        .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 (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.startswith("-")) { +    m_suffix = 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 = 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) { +    found_suffix = true; +    m_suffix = original_args; +  } +} diff --git a/lldb/source/Utility/Baton.cpp b/lldb/source/Utility/Baton.cpp new file mode 100644 index 0000000000000..84e295e246864 --- /dev/null +++ b/lldb/source/Utility/Baton.cpp @@ -0,0 +1,12 @@ +//===-- Baton.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/Baton.h" + +void lldb_private::UntypedBaton::GetDescription( +    Stream *s, lldb::DescriptionLevel level) const {} diff --git a/lldb/source/Utility/Broadcaster.cpp b/lldb/source/Utility/Broadcaster.cpp new file mode 100644 index 0000000000000..148fdf7ba5b1e --- /dev/null +++ b/lldb/source/Utility/Broadcaster.cpp @@ -0,0 +1,469 @@ +//===-- Broadcaster.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/Broadcaster.h" + +#include "lldb/Utility/Event.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" + +#include <algorithm> +#include <memory> +#include <type_traits> +#include <utility> + +#include <assert.h> +#include <stddef.h> + +using namespace lldb; +using namespace lldb_private; + +Broadcaster::Broadcaster(BroadcasterManagerSP manager_sp, const char *name) +    : m_broadcaster_sp(std::make_shared<BroadcasterImpl>(*this)), +      m_manager_sp(std::move(manager_sp)), m_broadcaster_name(name) { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); +  LLDB_LOG(log, "{0} Broadcaster::Broadcaster(\"{1}\")", +           static_cast<void *>(this), GetBroadcasterName().AsCString()); +} + +Broadcaster::BroadcasterImpl::BroadcasterImpl(Broadcaster &broadcaster) +    : m_broadcaster(broadcaster), m_listeners(), m_listeners_mutex(), +      m_hijacking_listeners(), m_hijacking_masks() {} + +Broadcaster::~Broadcaster() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); +  LLDB_LOG(log, "{0} Broadcaster::~Broadcaster(\"{1}\")", +           static_cast<void *>(this), GetBroadcasterName().AsCString()); + +  Clear(); +} + +void Broadcaster::CheckInWithManager() { +  if (m_manager_sp) { +    m_manager_sp->SignUpListenersForBroadcaster(*this); +  } +} + +llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> +Broadcaster::BroadcasterImpl::GetListeners() { +  llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> listeners; +  listeners.reserve(m_listeners.size()); + +  for (auto it = m_listeners.begin(); it != m_listeners.end();) { +    lldb::ListenerSP curr_listener_sp(it->first.lock()); +    if (curr_listener_sp && it->second) { +      listeners.emplace_back(std::move(curr_listener_sp), it->second); +      ++it; +    } else +      it = m_listeners.erase(it); +  } + +  return listeners; +} + +void Broadcaster::BroadcasterImpl::Clear() { +  std::lock_guard<std::recursive_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(); +} + +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::recursive_mutex> guard(m_listeners_mutex); + +  // See if we already have this listener, and if so, update its mask + +  bool handled = false; + +  for (auto &pair : GetListeners()) { +    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::recursive_mutex> guard(m_listeners_mutex); + +  if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back()) +    return true; + +  for (auto &pair : GetListeners()) { +    if (pair.second & event_type) +      return true; +  } +  return false; +} + +bool Broadcaster::BroadcasterImpl::RemoveListener( +    lldb_private::Listener *listener, uint32_t event_mask) { +  if (!listener) +    return false; + +  std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); +  for (auto &pair : GetListeners()) { +    if (pair.first.get() == listener) { +      pair.second &= ~event_mask; +      return true; +    } +  } +  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::recursive_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(); +  } + +  if (Log *log = lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS)) { +    StreamString event_description; +    event_sp->Dump(&event_description); +    LLDB_LOGF(log, +              "%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, " +              "unique =%i) hijack = %p", +              static_cast<void *>(this), GetBroadcasterName(), +              event_description.GetData(), unique, +              static_cast<void *>(hijacking_listener_sp.get())); +  } + +  if (hijacking_listener_sp) { +    if (unique && hijacking_listener_sp->PeekAtNextEventForBroadcasterWithType( +                      &m_broadcaster, event_type)) +      return; +    hijacking_listener_sp->AddEvent(event_sp); +  } else { +    for (auto &pair : GetListeners()) { +      if (!(pair.second & event_type)) +        continue; +      if (unique && pair.first->PeekAtNextEventForBroadcasterWithType( +                        &m_broadcaster, event_type)) +        continue; + +      pair.first->AddEvent(event_sp); +    } +  } +} + +void Broadcaster::BroadcasterImpl::BroadcastEvent(uint32_t event_type, +                                                  EventData *event_data) { +  auto event_sp = std::make_shared<Event>(event_type, event_data); +  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, EventData *event_data) { +  auto event_sp = std::make_shared<Event>(event_type, event_data); +  PrivateBroadcastEvent(event_sp, true); +} + +bool Broadcaster::BroadcasterImpl::HijackBroadcaster( +    const lldb::ListenerSP &listener_sp, uint32_t event_mask) { +  std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); + +  Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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::recursive_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::recursive_mutex> guard(m_listeners_mutex); + +  if (!m_hijacking_listeners.empty()) { +    ListenerSP listener_sp = m_hijacking_listeners.back(); +    Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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(); +} + +ConstString &Broadcaster::GetBroadcasterClass() const { +  static ConstString 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(); +} + +BroadcastEventSpec &BroadcastEventSpec:: +operator=(const BroadcastEventSpec &rhs) = default; + +BroadcasterManager::BroadcasterManager() : m_manager_mutex() {} + +lldb::BroadcasterManagerSP BroadcasterManager::MakeBroadcasterManager() { +  return lldb::BroadcasterManagerSP(new BroadcasterManager()); +} + +uint32_t BroadcasterManager::RegisterListenerForEvents( +    const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); + +  collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); +  uint32_t available_bits = event_spec.GetEventBits(); + +  while (iter != end_iter && +         (iter = find_if(iter, end_iter, +                         BroadcasterClassMatches( +                             event_spec.GetBroadcasterClass()))) != 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::UnregisterListenerForEvents( +    const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); +  bool removed_some = false; + +  if (m_listeners.erase(listener_sp) == 0) +    return false; + +  ListenerMatchesAndSharedBits predicate(event_spec, 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: +  while (true) { +    collection::iterator iter, end_iter = m_event_map.end(); +    iter = find_if(m_event_map.begin(), end_iter, predicate); +    if (iter == end_iter) { +      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.push_back( +          BroadcastEventSpec(event_spec.GetBroadcasterClass(), new_event_bits)); +    } +    m_event_map.erase(iter); +  } + +  // Okay now add back the bits that weren't completely removed: +  for (size_t i = 0; i < to_be_readded.size(); i++) { +    m_event_map.insert(event_listener_key(to_be_readded[i], listener_sp)); +  } + +  return removed_some; +} + +ListenerSP BroadcasterManager::GetListenerForEventSpec( +    const BroadcastEventSpec &event_spec) const { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); + +  collection::const_iterator iter, end_iter = m_event_map.end(); +  iter = find_if(m_event_map.begin(), end_iter, +                 BroadcastEventSpecMatches(event_spec)); +  if (iter != end_iter) +    return (*iter).second; + +  return nullptr; +} + +void BroadcasterManager::RemoveListener(Listener *listener) { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); +  ListenerMatchesPointer predicate(listener); +  listener_collection::iterator iter = m_listeners.begin(), +                                end_iter = m_listeners.end(); + +  std::find_if(iter, end_iter, predicate); +  if (iter != end_iter) +    m_listeners.erase(iter); + +  while (true) { +    collection::iterator iter, end_iter = m_event_map.end(); +    iter = find_if(m_event_map.begin(), end_iter, predicate); +    if (iter == end_iter) +      break; + +    m_event_map.erase(iter); +  } +} + +void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); +  ListenerMatches predicate(listener_sp); + +  if (m_listeners.erase(listener_sp) == 0) +    return; + +  while (true) { +    collection::iterator iter, end_iter = m_event_map.end(); +    iter = find_if(m_event_map.begin(), end_iter, predicate); +    if (iter == end_iter) +      break; + +    m_event_map.erase(iter); +  } +} + +void BroadcasterManager::SignUpListenersForBroadcaster( +    Broadcaster &broadcaster) { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); + +  collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + +  while (iter != end_iter && +         (iter = find_if(iter, end_iter, +                         BroadcasterClassMatches( +                             broadcaster.GetBroadcasterClass()))) != end_iter) { +    (*iter).second->StartListeningForEvents(&broadcaster, +                                            (*iter).first.GetEventBits()); +    iter++; +  } +} + +void BroadcasterManager::Clear() { +  std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); +  listener_collection::iterator end_iter = m_listeners.end(); + +  for (listener_collection::iterator iter = m_listeners.begin(); +       iter != end_iter; iter++) +    (*iter)->BroadcasterManagerWillDestruct(this->shared_from_this()); +  m_listeners.clear(); +  m_event_map.clear(); +} diff --git a/lldb/source/Utility/CompletionRequest.cpp b/lldb/source/Utility/CompletionRequest.cpp new file mode 100644 index 0000000000000..3b5a4570e3242 --- /dev/null +++ b/lldb/source/Utility/CompletionRequest.cpp @@ -0,0 +1,81 @@ +//===-- CompletionRequest.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/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.endswith(" ") && +      !GetCursorArgumentPrefix().endswith(" ")) +    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/lldb/source/Utility/Connection.cpp b/lldb/source/Utility/Connection.cpp new file mode 100644 index 0000000000000..483a0c941be44 --- /dev/null +++ b/lldb/source/Utility/Connection.cpp @@ -0,0 +1,13 @@ +//===-- Connection.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/Connection.h" + +using namespace lldb_private; + +Connection::~Connection() = default; diff --git a/lldb/source/Utility/ConstString.cpp b/lldb/source/Utility/ConstString.cpp new file mode 100644 index 0000000000000..2516ecf6a9897 --- /dev/null +++ b/lldb/source/Utility/ConstString.cpp @@ -0,0 +1,312 @@ +//===-- ConstString.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/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 <algorithm> +#include <array> +#include <utility> + +#include <inttypes.h> +#include <stdint.h> +#include <string.h> + +using namespace lldb_private; + +class Pool { +public: +  typedef const char *StringPoolValueType; +  typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator> +      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) const { +    if (ccstr != nullptr) { +      const uint8_t h = hash(llvm::StringRef(ccstr)); +      llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].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(const llvm::StringRef &string_ref) { +    if (string_ref.data()) { +      const uint8_t h = hash(string_ref); + +      { +        llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex); +        auto it = m_string_pools[h].m_string_map.find(string_ref); +        if (it != m_string_pools[h].m_string_map.end()) +          return it->getKeyData(); +      } + +      llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); +      StringPoolEntryType &entry = +          *m_string_pools[h] +               .m_string_map.insert(std::make_pair(string_ref, nullptr)) +               .first; +      return entry.getKeyData(); +    } +    return nullptr; +  } + +  const char * +  GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled, +                                          const char *mangled_ccstr) { +    const char *demangled_ccstr = nullptr; + +    { +      const uint8_t h = hash(demangled); +      llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); + +      // Make or update string pool entry with the mangled counterpart +      StringPool &map = m_string_pools[h].m_string_map; +      StringPoolEntryType &entry = *map.try_emplace(demangled).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... +      const uint8_t h = hash(llvm::StringRef(mangled_ccstr)); +      llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].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; +  } + +  // Return the size in bytes that this object and any items in its collection +  // of uniqued strings + data count values takes in memory. +  size_t MemorySize() const { +    size_t mem_size = sizeof(Pool); +    for (const auto &pool : m_string_pools) { +      llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex); +      for (const auto &entry : pool.m_string_map) +        mem_size += sizeof(StringPoolEntryType) + entry.getKey().size(); +    } +    return mem_size; +  } + +protected: +  uint8_t hash(const llvm::StringRef &s) const { +    uint32_t h = llvm::djbHash(s); +    return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff; +  } + +  struct PoolEntry { +    mutable llvm::sys::SmartRWMutex<false> m_mutex; +    StringPool m_string_map; +  }; + +  std::array<PoolEntry, 256> m_string_pools; +}; + +// 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(const 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_lower(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_lower(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(const llvm::StringRef &s) { +  m_string = StringPool().GetConstCStringWithLength(s.data(), s.size()); +} + +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); +} + +size_t ConstString::StaticMemorySize() { +  // Get the size of the static string pool +  return StringPool().MemorySize(); +} + +void llvm::format_provider<ConstString>::format(const ConstString &CS, +                                                llvm::raw_ostream &OS, +                                                llvm::StringRef Options) { +  format_provider<StringRef>::format(CS.AsCString(), OS, Options); +} diff --git a/lldb/source/Utility/DataBufferHeap.cpp b/lldb/source/Utility/DataBufferHeap.cpp new file mode 100644 index 0000000000000..5bff7775f138b --- /dev/null +++ b/lldb/source/Utility/DataBufferHeap.cpp @@ -0,0 +1,70 @@ +//===-- DataBufferHeap.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/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); +} + +// Virtual destructor since this class inherits from a pure virtual base class. +DataBufferHeap::~DataBufferHeap() = default; + +// Return a pointer to the bytes owned by this object, or nullptr if the object +// contains no bytes. +uint8_t *DataBufferHeap::GetBytes() { +  return (m_data.empty() ? nullptr : m_data.data()); +} + +// Return a const pointer to the bytes owned by this object, or nullptr if the +// object contains no bytes. +const uint8_t *DataBufferHeap::GetBytes() 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) { +  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); +} diff --git a/lldb/source/Utility/DataBufferLLVM.cpp b/lldb/source/Utility/DataBufferLLVM.cpp new file mode 100644 index 0000000000000..4227e9b39960f --- /dev/null +++ b/lldb/source/Utility/DataBufferLLVM.cpp @@ -0,0 +1,39 @@ +//===--- DataBufferLLVM.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/DataBufferLLVM.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <assert.h> +#include <type_traits> + +using namespace lldb_private; + +DataBufferLLVM::DataBufferLLVM( +    std::unique_ptr<llvm::WritableMemoryBuffer> MemBuffer) +    : Buffer(std::move(MemBuffer)) { +  assert(Buffer != nullptr && +         "Cannot construct a DataBufferLLVM with a null buffer"); +} + +DataBufferLLVM::~DataBufferLLVM() {} + +uint8_t *DataBufferLLVM::GetBytes() { +  return reinterpret_cast<uint8_t *>(Buffer->getBufferStart()); +} + +const uint8_t *DataBufferLLVM::GetBytes() const { +  return reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()); +} + +lldb::offset_t DataBufferLLVM::GetByteSize() const { +  return Buffer->getBufferSize(); +} diff --git a/lldb/source/Utility/DataEncoder.cpp b/lldb/source/Utility/DataEncoder.cpp new file mode 100644 index 0000000000000..13c505e34e821 --- /dev/null +++ b/lldb/source/Utility/DataEncoder.cpp @@ -0,0 +1,235 @@ +//===-- DataEncoder.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/DataEncoder.h" + +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Endian.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" + +#include <cassert> +#include <cstddef> + +#include <string.h> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::support::endian; + +// Default constructor. +DataEncoder::DataEncoder() +    : m_start(nullptr), m_end(nullptr), +      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. +DataEncoder::DataEncoder(void *data, uint32_t length, ByteOrder endian, +                         uint8_t addr_size) +    : m_start(static_cast<uint8_t *>(data)), +      m_end(static_cast<uint8_t *>(data) + length), m_byte_order(endian), +      m_addr_size(addr_size), m_data_sp() {} + +// 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 DataEncoder +// objects exist that have a reference to this data. +DataEncoder::DataEncoder(const DataBufferSP &data_sp, ByteOrder endian, +                         uint8_t addr_size) +    : m_start(nullptr), m_end(nullptr), m_byte_order(endian), +      m_addr_size(addr_size), m_data_sp() { +  SetData(data_sp); +} + +DataEncoder::~DataEncoder() = default; + +// Clears the object contents back to a default invalid state, and release any +// references to shared data that this object may contain. +void DataEncoder::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 DataEncoder::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. +uint32_t DataEncoder::SetData(void *bytes, uint32_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 = static_cast<uint8_t *>(bytes); +    m_end = m_start + length; +  } +  return GetByteSize(); +} + +// 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. +uint32_t DataEncoder::SetData(const DataBufferSP &data_sp, uint32_t data_offset, +                              uint32_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 +      } +    } +  } + +  uint32_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. +uint32_t DataEncoder::PutU8(uint32_t offset, uint8_t value) { +  if (ValidOffset(offset)) { +    m_start[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_start + offset, value); +    else +      write16le(m_start + 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_start + offset, value); +    else +      write32le(m_start + 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_start + offset, value); +    else +      write64le(m_start + offset, value); + +    return offset + sizeof(value); +  } +  return UINT32_MAX; +} + +// Extract a single integer value from the data and update the offset pointed +// to by "offset_ptr". The size of the extracted integer is specified by the +// "byte_size" argument. "byte_size" should have a value >= 1 and <= 8 since +// the return value is only 64 bits wide. Any "byte_size" values less than 1 or +// greater than 8 will result in nothing being extracted, and zero being +// returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +uint32_t DataEncoder::PutMaxU64(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_start + offset, src, src_len); +    return offset + src_len; +  } +  return UINT32_MAX; +} + +uint32_t DataEncoder::PutAddress(uint32_t offset, lldb::addr_t addr) { +  return PutMaxU64(offset, GetAddressByteSize(), addr); +} + +uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) { +  if (cstr != nullptr) +    return PutData(offset, cstr, strlen(cstr) + 1); +  return UINT32_MAX; +} diff --git a/lldb/source/Utility/DataExtractor.cpp b/lldb/source/Utility/DataExtractor.cpp new file mode 100644 index 0000000000000..f642a8fc76392 --- /dev/null +++ b/lldb/source/Utility/DataExtractor.cpp @@ -0,0 +1,1120 @@ +//===-- DataExtractor.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/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/Endian.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/Support/MD5.h" +#include "llvm/Support/MathExtras.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstdint> +#include <string> + +#include <ctype.h> +#include <inttypes.h> +#include <string.h> + +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_16(value); +} + +static inline uint32_t ReadSwapInt32(const unsigned char *ptr, +                                     offset_t offset) { +  uint32_t value; +  memcpy(&value, ptr + offset, 4); +  return llvm::ByteSwap_32(value); +} + +static inline uint64_t ReadSwapInt64(const unsigned char *ptr, +                                     offset_t offset) { +  uint64_t value; +  memcpy(&value, ptr + offset, 8); +  return llvm::ByteSwap_64(value); +} + +static inline uint16_t ReadSwapInt16(const void *ptr) { +  uint16_t value; +  memcpy(&value, ptr, 2); +  return llvm::ByteSwap_16(value); +} + +static inline uint32_t ReadSwapInt32(const void *ptr) { +  uint32_t value; +  memcpy(&value, ptr, 4); +  return llvm::ByteSwap_32(value); +} + +static inline uint64_t ReadSwapInt64(const void *ptr) { +  uint64_t value; +  memcpy(&value, ptr, 8); +  return llvm::ByteSwap_64(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_start(nullptr), m_end(nullptr), +      m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)), +      m_data_sp(), m_target_byte_size(1) {} + +// 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 *>(reinterpret_cast<const uint8_t *>(data))), +      m_end(const_cast<uint8_t *>(reinterpret_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 == 4 || 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_start(nullptr), m_end(nullptr), m_byte_order(endian), +      m_addr_size(addr_size), m_data_sp(), +      m_target_byte_size(target_byte_size) { +  assert(addr_size == 4 || 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_start(nullptr), m_end(nullptr), 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 == 4 || 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 == 4 || 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 *>(reinterpret_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 == 4 || 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 { +  uint64_t uval64 = GetMaxU64(offset_ptr, size); +  if (bitfield_bit_size > 0) { +    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 = ((1ul << 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 { +  int64_t sval64 = GetMaxS64(offset_ptr, size); +  if (bitfield_bit_size > 0) { +    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 = +        ((static_cast<uint64_t>(1)) << bitfield_bit_size) - 1; +    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 { +  typedef float float_type; +  float_type val = 0.0; +  const size_t src_size = sizeof(float_type); +  const float_type *src = +      static_cast<const float_type *>(GetData(offset_ptr, src_size)); +  if (src) { +    if (m_byte_order != endian::InlHostByteOrder()) { +      const uint8_t *src_data = reinterpret_cast<const uint8_t *>(src); +      uint8_t *dst_data = reinterpret_cast<uint8_t *>(&val); +      for (size_t i = 0; i < sizeof(float_type); ++i) +        dst_data[sizeof(float_type) - 1 - i] = src_data[i]; +    } else { +      val = *src; +    } +  } +  return val; +} + +double DataExtractor::GetDouble(offset_t *offset_ptr) const { +  typedef double float_type; +  float_type val = 0.0; +  const size_t src_size = sizeof(float_type); +  const float_type *src = +      static_cast<const float_type *>(GetData(offset_ptr, src_size)); +  if (src) { +    if (m_byte_order != endian::InlHostByteOrder()) { +      const uint8_t *src_data = reinterpret_cast<const uint8_t *>(src); +      uint8_t *dst_data = reinterpret_cast<uint8_t *>(&val); +      for (size_t i = 0; i < sizeof(float_type); ++i) +        dst_data[sizeof(float_type) - 1 - i] = src_data[i]; +    } else { +      val = *src; +    } +  } +  return val; +} + +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 == 4 || 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 == 4 || m_addr_size == 8); +  return GetMaxU64_unchecked(offset_ptr, m_addr_size); +} + +// Extract a single pointer from the data and update the offset pointed to by +// "offset_ptr". The size of the extracted pointer comes from the +// "this->m_addr_size" member variable and should be set correctly prior to +// extracting any pointer values. +// +// RETURNS the pointer that was extracted, or zero on failure. +uint64_t DataExtractor::GetPointer(offset_t *offset_ptr) const { +  assert(m_addr_size == 4 || m_addr_size == 8); +  return GetMaxU64(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; + +  const uint8_t *end = m_end; + +  if (src < end) { +    uint64_t result = *src++; +    if (result >= 0x80) { +      result &= 0x7f; +      int shift = 7; +      while (src < end) { +        uint8_t byte = *src++; +        result |= static_cast<uint64_t>(byte & 0x7f) << shift; +        if ((byte & 0x80) == 0) +          break; +        shift += 7; +      } +    } +    *offset_ptr = src - m_start; +    return result; +  } + +  return 0; +} + +// 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; + +  const uint8_t *end = m_end; + +  if (src < end) { +    int64_t result = 0; +    int shift = 0; +    int size = sizeof(int64_t) * 8; + +    uint8_t byte = 0; +    int bytecount = 0; + +    while (src < end) { +      bytecount++; +      byte = *src++; +      result |= static_cast<int64_t>(byte & 0x7f) << shift; +      shift += 7; +      if ((byte & 0x80) == 0) +        break; +    } + +    // Sign bit of byte is 2nd high order bit (0x40) +    if (shift < size && (byte & 0x40)) +      result |= -(1 << shift); + +    *offset_ptr += bytecount; +    return result; +  } +  return 0; +} + +// 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 char *format) 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(format ? format : " %2.2x", GetU8(&offset)); +      break; +    case TypeChar: { +      char ch = GetU8(&offset); +      sstr.Printf(format ? format : " %c", isprint(ch) ? ch : ' '); +    } break; +    case TypeUInt16: +      sstr.Printf(format ? format : " %4.4x", GetU16(&offset)); +      break; +    case TypeUInt32: +      sstr.Printf(format ? format : " %8.8x", GetU32(&offset)); +      break; +    case TypeUInt64: +      sstr.Printf(format ? format : " %16.16" PRIx64, GetU64(&offset)); +      break; +    case TypePointer: +      sstr.Printf(format ? format : " 0x%" PRIx64, GetAddress(&offset)); +      break; +    case TypeULEB128: +      sstr.Printf(format ? format : " 0x%" PRIx64, GetULEB128(&offset)); +      break; +    case TypeSLEB128: +      sstr.Printf(format ? format : " %" 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.Bytes.begin(), result.Bytes.end()); +} diff --git a/lldb/source/Utility/Environment.cpp b/lldb/source/Utility/Environment.cpp new file mode 100644 index 0000000000000..1405336007124 --- /dev/null +++ b/lldb/source/Utility/Environment.cpp @@ -0,0 +1,49 @@ +//===-- Environment.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/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 = reinterpret_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 = reinterpret_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(const_iterator first, const_iterator last) { +  while (first != last) { +    try_emplace(first->first(), first->second); +    ++first; +  } +} diff --git a/lldb/source/Utility/Event.cpp b/lldb/source/Utility/Event.cpp new file mode 100644 index 0000000000000..579d0dac86ed6 --- /dev/null +++ b/lldb/source/Utility/Event.cpp @@ -0,0 +1,288 @@ +//===-- Event.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/Event.h" + +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-enumerations.h" + +#include <algorithm> + +#include <ctype.h> + +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().GetCString(), 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().GetCString(), 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() { +  if (m_data_sp) +    m_data_sp->DoOnRemoval(this); +} + +#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(const char *cstr) : m_bytes() { +  SetBytesFromCString(cstr); +} + +EventDataBytes::EventDataBytes(llvm::StringRef str) : m_bytes() { +  SetBytes(str.data(), str.size()); +} + +EventDataBytes::EventDataBytes(const void *src, size_t src_len) : m_bytes() { +  SetBytes(src, src_len); +} + +EventDataBytes::~EventDataBytes() = default; + +ConstString EventDataBytes::GetFlavorString() { +  static ConstString g_flavor("EventDataBytes"); +  return g_flavor; +} + +ConstString EventDataBytes::GetFlavor() const { +  return EventDataBytes::GetFlavorString(); +} + +void EventDataBytes::Dump(Stream *s) const { +  size_t num_printable_chars = +      std::count_if(m_bytes.begin(), m_bytes.end(), isprint); +  if (num_printable_chars == m_bytes.size()) +    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(); } + +void EventDataBytes::SetBytes(const void *src, size_t src_len) { +  if (src != nullptr && src_len > 0) +    m_bytes.assign(static_cast<const char *>(src), src_len); +  else +    m_bytes.clear(); +} + +void EventDataBytes::SetBytesFromCString(const char *cstr) { +  if (cstr != nullptr && cstr[0]) +    m_bytes.assign(cstr); +  else +    m_bytes.clear(); +} + +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; +} + +void EventDataBytes::SwapBytes(std::string &new_bytes) { +  m_bytes.swap(new_bytes); +} + +#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() {} + +// EventDataStructuredData member functions + +ConstString 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(); +} + +ConstString EventDataStructuredData::GetFlavorString() { +  static ConstString s_flavor("EventDataStructuredData"); +  return s_flavor; +} diff --git a/lldb/source/Utility/FileSpec.cpp b/lldb/source/Utility/FileSpec.cpp new file mode 100644 index 0000000000000..f22ab4d84e40a --- /dev/null +++ b/lldb/source/Utility/FileSpec.cpp @@ -0,0 +1,562 @@ +//===-- FileSpec.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/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/StringRef.h" +#include "llvm/ADT/Triple.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 <algorithm> +#include <system_error> +#include <vector> + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +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 (style == FileSpec::Style::posix || +          (style == FileSpec::Style::native && +           GetNativeStyle() == FileSpec::Style::posix)); +} + +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} {} + +// Copy constructor +FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() { +  if (rhs) +    *this = *rhs; +} + +// Virtual destructor in case anyone inherits from this class. +FileSpec::~FileSpec() {} + +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; +} + + +} +// Assignment operator. +const FileSpec &FileSpec::operator=(const FileSpec &rhs) { +  if (this != &rhs) { +    m_directory = rhs.m_directory; +    m_filename = rhs.m_filename; +    m_is_resolved = rhs.m_is_resolved; +    m_style = rhs.m_style; +  } +  return *this; +} + +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) { +  m_filename.Clear(); +  m_directory.Clear(); +  m_is_resolved = false; +  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); +  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(); +} + +// 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) { +  // case sensitivity of equality test +  const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); + +  const bool filenames_equal = ConstString::Equals(a.m_filename, +                                                   b.m_filename, +                                                   case_sensitive); + +  if (!filenames_equal) +    return false; + +  if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) +    return filenames_equal; + +  return a == b; +} + +llvm::Optional<FileSpec::Style> FileSpec::GuessPathStyle(llvm::StringRef absolute_path) { +  if (absolute_path.startswith("/")) +    return Style::posix; +  if (absolute_path.startswith(R"(\\)")) +    return Style::windows; +  if (absolute_path.size() > 3 && llvm::isAlpha(absolute_path[0]) && +      absolute_path.substr(1, 2) == R"(:\)") +    return Style::windows; +  return llvm::None; +} + +// 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(Stream *s) const { +  if (s) { +    std::string path{GetPath(true)}; +    s->PutCString(path); +    char path_separator = GetPreferredPathSeparator(m_style); +    if (!m_filename && !path.empty() && path.back() != path_separator) +      s->PutChar(path_separator); +  } +} + +FileSpec::Style FileSpec::GetPathStyle() const { return m_style; } + +// Directory string get accessor. +ConstString &FileSpec::GetDirectory() { return m_directory; } + +// Directory string const get accessor. +ConstString FileSpec::GetDirectory() const { return m_directory; } + +// Filename string get accessor. +ConstString &FileSpec::GetFilename() { return m_filename; } + +// Filename string const get accessor. +ConstString FileSpec::GetFilename() const { return m_filename; } + +// 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 std::string(result.begin(), result.end()); +} + +const char *FileSpec::GetCString(bool denormalize) const { +  return ConstString{GetPath(denormalize)}.AsCString(nullptr); +} + +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); +} + +ConstString FileSpec::GetFileNameExtension() const { +  return ConstString( +      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; +} + +ConstString FileSpec::GetLastPathComponent() const { +  llvm::SmallString<64> current_path; +  GetPath(current_path, false); +  return ConstString(llvm::sys::path::filename(current_path, m_style)); +} + +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; +} +/// 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 { +  ConstString extension(GetFileNameExtension()); +  if (!extension) +    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.GetStringRef()); +} + +bool FileSpec::IsRelative() const { +  return !IsAbsolute(); +} + +bool FileSpec::IsAbsolute() const { +  llvm::SmallString<64> current_path; +  GetPath(current_path, false); + +  // Early return if the path is empty. +  if (current_path.empty()) +    return false; + +  // We consider paths starting with ~ to be absolute. +  if (current_path[0] == '~') +    return true; + +  return llvm::sys::path::is_absolute(current_path, m_style); +} + +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_lower("F") || Style.equals_lower("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_lower("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_lower("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/lldb/source/Utility/GDBRemote.cpp b/lldb/source/Utility/GDBRemote.cpp new file mode 100644 index 0000000000000..85c4bc69a8d10 --- /dev/null +++ b/lldb/source/Utility/GDBRemote.cpp @@ -0,0 +1,105 @@ +//===-- 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 <stdio.h> + +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() {} + +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; +} + +void GDBRemotePacket::Serialize(raw_ostream &strm) const { +  yaml::Output yout(strm); +  yout << const_cast<GDBRemotePacket &>(*this); +  strm.flush(); +} + +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()); +} + +void yaml::ScalarEnumerationTraits<GDBRemotePacket::Type>::enumeration( +    IO &io, GDBRemotePacket::Type &value) { +  io.enumCase(value, "Invalid", GDBRemotePacket::ePacketTypeInvalid); +  io.enumCase(value, "Send", GDBRemotePacket::ePacketTypeSend); +  io.enumCase(value, "Recv", GDBRemotePacket::ePacketTypeRecv); +} + +void yaml::ScalarTraits<GDBRemotePacket::BinaryData>::output( +    const GDBRemotePacket::BinaryData &Val, void *, raw_ostream &Out) { +  Out << toHex(Val.data); +} + +StringRef yaml::ScalarTraits<GDBRemotePacket::BinaryData>::input( +    StringRef Scalar, void *, GDBRemotePacket::BinaryData &Val) { +  Val.data = fromHex(Scalar); +  return {}; +} + +void yaml::MappingTraits<GDBRemotePacket>::mapping(IO &io, +                                                   GDBRemotePacket &Packet) { +  io.mapRequired("packet", Packet.packet); +  io.mapRequired("type", Packet.type); +  io.mapRequired("bytes", Packet.bytes_transmitted); +  io.mapRequired("index", Packet.packet_idx); +  io.mapRequired("tid", Packet.tid); +} + +StringRef +yaml::MappingTraits<GDBRemotePacket>::validate(IO &io, +                                               GDBRemotePacket &Packet) { +  if (Packet.bytes_transmitted != Packet.packet.data.size()) +    return "BinaryData size doesn't match bytes transmitted"; + +  return {}; +} diff --git a/lldb/source/Utility/IOObject.cpp b/lldb/source/Utility/IOObject.cpp new file mode 100644 index 0000000000000..5e3ccddb6a307 --- /dev/null +++ b/lldb/source/Utility/IOObject.cpp @@ -0,0 +1,14 @@ +//===-- IOObject.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/IOObject.h" + +using namespace lldb_private; + +const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1; +IOObject::~IOObject() = default; diff --git a/lldb/source/Utility/LLDBAssert.cpp b/lldb/source/Utility/LLDBAssert.cpp new file mode 100644 index 0000000000000..75530d8063101 --- /dev/null +++ b/lldb/source/Utility/LLDBAssert.cpp @@ -0,0 +1,36 @@ +//===--------------------- LLDBAssert.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/LLDBAssert.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace lldb_private; + +void lldb_private::lldb_assert(bool expression, const char *expr_text, +                               const char *func, const char *file, +                               unsigned int line) { +  if (LLVM_LIKELY(expression)) +    return; + +  // In a Debug configuration lldb_assert() behaves like assert(0). +  llvm_unreachable("lldb_assert failed"); + +  // In a Release configuration it will print a warning and encourage the user +  // to file a bug report, similar to LLVM’s crash handler, and then return +  // execution. +  errs() << format("Assertion failed: (%s), function %s, file %s, line %u\n", +                   expr_text, func, file, line); +  errs() << "backtrace leading to the failure:\n"; +  llvm::sys::PrintStackTrace(errs()); +  errs() << "please file a bug report against lldb reporting this failure " +            "log, and as many details as possible\n"; +} diff --git a/lldb/source/Utility/Listener.cpp b/lldb/source/Utility/Listener.cpp new file mode 100644 index 0000000000000..c2e537ba7dee0 --- /dev/null +++ b/lldb/source/Utility/Listener.cpp @@ -0,0 +1,466 @@ +//===-- Listener.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/Listener.h" + +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" + +#include "llvm/ADT/Optional.h" + +#include <algorithm> +#include <memory> +#include <utility> + +using namespace lldb; +using namespace lldb_private; + +namespace { +class BroadcasterManagerWPMatcher { +public: +  BroadcasterManagerWPMatcher(BroadcasterManagerSP manager_sp) +      : m_manager_sp(std::move(manager_sp)) {} +  bool operator()(const BroadcasterManagerWP &input_wp) const { +    BroadcasterManagerSP input_sp = input_wp.lock(); +    return (input_sp && input_sp == m_manager_sp); +  } + +  BroadcasterManagerSP m_manager_sp; +}; +} // anonymous namespace + +Listener::Listener(const char *name) +    : m_name(name), m_broadcasters(), m_broadcasters_mutex(), m_events(), +      m_events_mutex() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); +  if (log != nullptr) +    LLDB_LOGF(log, "%p Listener::Listener('%s')", static_cast<void *>(this), +              m_name.c_str()); +} + +Listener::~Listener() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + +  Clear(); + +  LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this), +            __FUNCTION__, m_name.c_str()); +} + +void Listener::Clear() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); +  std::lock_guard<std::recursive_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::recursive_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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::recursive_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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::recursive_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::recursive_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) { +  // Just need to remove this broadcast manager from the list of managers: +  broadcaster_manager_collection::iterator iter, +      end_iter = m_broadcaster_managers.end(); +  BroadcasterManagerWP manager_wp; + +  BroadcasterManagerWPMatcher matcher(std::move(manager_sp)); +  iter = std::find_if<broadcaster_manager_collection::iterator, +                      BroadcasterManagerWPMatcher>( +      m_broadcaster_managers.begin(), end_iter, matcher); +  if (iter != end_iter) +    m_broadcaster_managers.erase(iter); +} + +void Listener::AddEvent(EventSP &event_sp) { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(); +} + +class EventBroadcasterMatches { +public: +  EventBroadcasterMatches(Broadcaster *broadcaster) +      : m_broadcaster(broadcaster) {} + +  bool operator()(const EventSP &event_sp) const { +    return event_sp->BroadcasterIs(m_broadcaster); +  } + +private: +  Broadcaster *m_broadcaster; +}; + +class EventMatcher { +public: +  EventMatcher(Broadcaster *broadcaster, const ConstString *broadcaster_names, +               uint32_t num_broadcaster_names, uint32_t event_type_mask) +      : m_broadcaster(broadcaster), m_broadcaster_names(broadcaster_names), +        m_num_broadcaster_names(num_broadcaster_names), +        m_event_type_mask(event_type_mask) {} + +  bool operator()(const EventSP &event_sp) const { +    if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster)) +      return false; + +    if (m_broadcaster_names) { +      bool found_source = false; +      ConstString event_broadcaster_name = +          event_sp->GetBroadcaster()->GetBroadcasterName(); +      for (uint32_t i = 0; i < m_num_broadcaster_names; ++i) { +        if (m_broadcaster_names[i] == event_broadcaster_name) { +          found_source = true; +          break; +        } +      } +      if (!found_source) +        return false; +    } + +    return m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType(); +  } + +private: +  Broadcaster *m_broadcaster; +  const ConstString *m_broadcaster_names; +  const uint32_t m_num_broadcaster_names; +  const uint32_t m_event_type_mask; +}; + +bool Listener::FindNextEventInternal( +    std::unique_lock<std::mutex> &lock, +    Broadcaster *broadcaster,             // nullptr for any broadcaster +    const ConstString *broadcaster_names, // nullptr for any event +    uint32_t num_broadcaster_names, 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); + +  if (m_events.empty()) +    return false; + +  Listener::event_collection::iterator pos = m_events.end(); + +  if (broadcaster == nullptr && broadcaster_names == nullptr && +      event_type_mask == 0) { +    pos = m_events.begin(); +  } else { +    pos = std::find_if(m_events.begin(), m_events.end(), +                       EventMatcher(broadcaster, broadcaster_names, +                                    num_broadcaster_names, event_type_mask)); +  } + +  if (pos != m_events.end()) { +    event_sp = *pos; + +    if (log != nullptr) +      LLDB_LOGF(log, +                "%p '%s' Listener::FindNextEventInternal(broadcaster=%p, " +                "broadcaster_names=%p[%u], event_type_mask=0x%8.8x, " +                "remove=%i) event %p", +                static_cast<void *>(this), GetName(), +                static_cast<void *>(broadcaster), +                static_cast<const void *>(broadcaster_names), +                num_broadcaster_names, 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, nullptr, 0, 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, nullptr, 0, 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, nullptr, 0, 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 +    const ConstString *broadcaster_names, // nullptr for any event +    uint32_t num_broadcaster_names, uint32_t event_type_mask, +    EventSP &event_sp) { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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, broadcaster_names, +                              num_broadcaster_names, 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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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, nullptr, 0, event_type_mask, +                          event_sp); +} + +bool Listener::GetEventForBroadcaster(Broadcaster *broadcaster, +                                      EventSP &event_sp, +                                      const Timeout<std::micro> &timeout) { +  return GetEventInternal(timeout, broadcaster, nullptr, 0, 0, event_sp); +} + +bool Listener::GetEvent(EventSP &event_sp, const Timeout<std::micro> &timeout) { +  return GetEventInternal(timeout, nullptr, nullptr, 0, 0, event_sp); +} + +size_t Listener::HandleBroadcastEvent(EventSP &event_sp) { +  size_t num_handled = 0; +  std::lock_guard<std::recursive_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; + +  // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to +  // avoid violating the lock hierarchy (manager before broadcasters). +  std::lock_guard<std::recursive_mutex> manager_guard( +      manager_sp->m_manager_mutex); +  std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex); + +  uint32_t bits_acquired = manager_sp->RegisterListenerForEvents( +      this->shared_from_this(), event_spec); +  if (bits_acquired) { +    broadcaster_manager_collection::iterator iter, +        end_iter = m_broadcaster_managers.end(); +    BroadcasterManagerWP manager_wp(manager_sp); +    BroadcasterManagerWPMatcher matcher(manager_sp); +    iter = std::find_if<broadcaster_manager_collection::iterator, +                        BroadcasterManagerWPMatcher>( +        m_broadcaster_managers.begin(), end_iter, matcher); +    if (iter == end_iter) +      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; + +  std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex); +  return manager_sp->UnregisterListenerForEvents(this->shared_from_this(), +                                                 event_spec); +} + +ListenerSP Listener::MakeListener(const char *name) { +  return ListenerSP(new Listener(name)); +} diff --git a/lldb/source/Utility/Log.cpp b/lldb/source/Utility/Log.cpp new file mode 100644 index 0000000000000..ab5e630114a6b --- /dev/null +++ b/lldb/source/Utility/Log.cpp @@ -0,0 +1,355 @@ +//===-- Log.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/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/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 <assert.h> +#if defined(_WIN32) +#include <process.h> +#else +#include <unistd.h> +#include <pthread.h> +#endif + +using namespace lldb_private; + +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); +                  }); +} + +uint32_t Log::GetFlags(llvm::raw_ostream &stream, const ChannelMap::value_type &entry, +                         llvm::ArrayRef<const char *> categories) { +  bool list_categories = false; +  uint32_t flags = 0; +  for (const char *category : categories) { +    if (llvm::StringRef("all").equals_lower(category)) { +      flags |= UINT32_MAX; +      continue; +    } +    if (llvm::StringRef("default").equals_lower(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_lower(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<llvm::raw_ostream> &stream_sp, +                 uint32_t options, uint32_t flags) { +  llvm::sys::ScopedWriter lock(m_mutex); + +  uint32_t mask = m_mask.fetch_or(flags, std::memory_order_relaxed); +  if (mask | flags) { +    m_options.store(options, std::memory_order_relaxed); +    m_stream_sp = stream_sp; +    m_channel.log_ptr.store(this, std::memory_order_relaxed); +  } +} + +void Log::Disable(uint32_t flags) { +  llvm::sys::ScopedWriter lock(m_mutex); + +  uint32_t mask = m_mask.fetch_and(~flags, std::memory_order_relaxed); +  if (!(mask & ~flags)) { +    m_stream_sp.reset(); +    m_channel.log_ptr.store(nullptr, std::memory_order_relaxed); +  } +} + +const Flags Log::GetOptions() const { +  return m_options.load(std::memory_order_relaxed); +} + +const Flags Log::GetMask() const { +  return m_mask.load(std::memory_order_relaxed); +} + +void Log::PutCString(const char *cstr) { Printf("%s", cstr); } +void Log::PutString(llvm::StringRef str) { PutCString(str.str().c_str()); } + +// 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); +} + +// All logging eventually boils down to this function call. 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::VAPrintf(const char *format, va_list args) { +  llvm::SmallString<64> FinalMessage; +  llvm::raw_svector_ostream Stream(FinalMessage); +  WriteHeader(Stream, "", ""); + +  llvm::SmallString<64> Content; +  lldb_private::VASprintf(Content, format, args); + +  Stream << Content << "\n"; + +  WriteMessage(FinalMessage.str()); +} + +// 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::Initialize() { +#ifdef LLVM_ON_UNIX +  pthread_atfork(nullptr, nullptr, &Log::DisableLoggingChild); +#endif +  InitializeLldbChannel(); +} + +void Log::Register(llvm::StringRef name, Channel &channel) { +  auto iter = g_channel_map->try_emplace(name, channel); +  assert(iter.second == true); +  (void)iter; +} + +void Log::Unregister(llvm::StringRef name) { +  auto iter = g_channel_map->find(name); +  assert(iter != g_channel_map->end()); +  iter->second.Disable(UINT32_MAX); +  g_channel_map->erase(iter); +} + +bool Log::EnableLogChannel( +    const std::shared_ptr<llvm::raw_ostream> &log_stream_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; +  } +  uint32_t flags = categories.empty() +                       ? iter->second.m_channel.default_flags +                       : GetFlags(error_stream, *iter, categories); +  iter->second.Enable(log_stream_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; +  } +  uint32_t flags = categories.empty() +                       ? UINT32_MAX +                       : GetFlags(error_stream, *iter, categories); +  iter->second.Disable(flags); +  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(UINT32_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()); +  } +} + +void Log::WriteMessage(const std::string &message) { +  // Make a copy of our stream shared pointer in case someone disables our log +  // while we are logging and releases the stream +  auto stream_sp = GetStream(); +  if (!stream_sp) +    return; + +  Flags options = GetOptions(); +  if (options.Test(LLDB_LOG_OPTION_THREADSAFE)) { +    static std::recursive_mutex g_LogThreadedMutex; +    std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex); +    *stream_sp << message; +    stream_sp->flush(); +  } else { +    *stream_sp << message; +    stream_sp->flush(); +  } +} + +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()); +} + +void Log::DisableLoggingChild() { +  // Disable logging by clearing out the atomic variable after forking -- if we +  // forked while another thread held the channel mutex, we would deadlock when +  // trying to write to the log. +  for (auto &c: *g_channel_map) +    c.second.m_channel.log_ptr.store(nullptr, std::memory_order_relaxed); +} diff --git a/lldb/source/Utility/Logging.cpp b/lldb/source/Utility/Logging.cpp new file mode 100644 index 0000000000000..22f38192fa5d1 --- /dev/null +++ b/lldb/source/Utility/Logging.cpp @@ -0,0 +1,64 @@ +//===-- Logging.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/Logging.h" +#include "lldb/Utility/Log.h" + +#include "llvm/ADT/ArrayRef.h" + +#include <stdarg.h> + +using namespace lldb_private; + +static constexpr Log::Category g_categories[] = { +  {{"api"}, {"log API calls and return values"}, LIBLLDB_LOG_API}, +  {{"ast"}, {"log AST"}, LIBLLDB_LOG_AST}, +  {{"break"}, {"log breakpoints"}, LIBLLDB_LOG_BREAKPOINTS}, +  {{"commands"}, {"log command argument parsing"}, LIBLLDB_LOG_COMMANDS}, +  {{"comm"}, {"log communication activities"}, LIBLLDB_LOG_COMMUNICATION}, +  {{"conn"}, {"log connection details"}, LIBLLDB_LOG_CONNECTION}, +  {{"demangle"}, {"log mangled names to catch demangler crashes"}, LIBLLDB_LOG_DEMANGLE}, +  {{"dyld"}, {"log shared library related activities"}, LIBLLDB_LOG_DYNAMIC_LOADER}, +  {{"event"}, {"log broadcaster, listener and event queue activities"}, LIBLLDB_LOG_EVENTS}, +  {{"expr"}, {"log expressions"}, LIBLLDB_LOG_EXPRESSIONS}, +  {{"formatters"}, {"log data formatters related activities"}, LIBLLDB_LOG_DATAFORMATTERS}, +  {{"host"}, {"log host activities"}, LIBLLDB_LOG_HOST}, +  {{"jit"}, {"log JIT events in the target"}, LIBLLDB_LOG_JIT_LOADER}, +  {{"language"}, {"log language runtime events"}, LIBLLDB_LOG_LANGUAGE}, +  {{"mmap"}, {"log mmap related activities"}, LIBLLDB_LOG_MMAP}, +  {{"module"}, {"log module activities such as when modules are created, destroyed, replaced, and more"}, LIBLLDB_LOG_MODULES}, +  {{"object"}, {"log object construction/destruction for important objects"}, LIBLLDB_LOG_OBJECT}, +  {{"os"}, {"log OperatingSystem plugin related activities"}, LIBLLDB_LOG_OS}, +  {{"platform"}, {"log platform events and activities"}, LIBLLDB_LOG_PLATFORM}, +  {{"process"}, {"log process events and activities"}, LIBLLDB_LOG_PROCESS}, +  {{"script"}, {"log events about the script interpreter"}, LIBLLDB_LOG_SCRIPT}, +  {{"state"}, {"log private and public process state changes"}, LIBLLDB_LOG_STATE}, +  {{"step"}, {"log step related activities"}, LIBLLDB_LOG_STEP}, +  {{"symbol"}, {"log symbol related issues and warnings"}, LIBLLDB_LOG_SYMBOLS}, +  {{"system-runtime"}, {"log system runtime events"}, LIBLLDB_LOG_SYSTEM_RUNTIME}, +  {{"target"}, {"log target events and activities"}, LIBLLDB_LOG_TARGET}, +  {{"temp"}, {"log internal temporary debug messages"}, LIBLLDB_LOG_TEMPORARY}, +  {{"thread"}, {"log thread events and activities"}, LIBLLDB_LOG_THREAD}, +  {{"types"}, {"log type system related activities"}, LIBLLDB_LOG_TYPES}, +  {{"unwind"}, {"log stack unwind activities"}, LIBLLDB_LOG_UNWIND}, +  {{"watch"}, {"log watchpoint related activities"}, LIBLLDB_LOG_WATCHPOINTS}, +}; + +static Log::Channel g_log_channel(g_categories, LIBLLDB_LOG_DEFAULT); + +void lldb_private::InitializeLldbChannel() { +  Log::Register("lldb", g_log_channel); +} + +Log *lldb_private::GetLogIfAllCategoriesSet(uint32_t mask) { +  return g_log_channel.GetLogIfAll(mask); +} + +Log *lldb_private::GetLogIfAnyCategoriesSet(uint32_t mask) { +  return g_log_channel.GetLogIfAny(mask); +} diff --git a/lldb/source/Utility/NameMatches.cpp b/lldb/source/Utility/NameMatches.cpp new file mode 100644 index 0000000000000..5c9579ea73329 --- /dev/null +++ b/lldb/source/Utility/NameMatches.cpp @@ -0,0 +1,34 @@ +//===-- NameMatches.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/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.startswith(match); +  case NameMatch::EndsWith: +    return name.endswith(match); +  case NameMatch::RegularExpression: { +    RegularExpression regex(match); +    return regex.Execute(name); +  } +  } +  return false; +} diff --git a/lldb/source/Utility/PPC64LE_DWARF_Registers.h b/lldb/source/Utility/PPC64LE_DWARF_Registers.h new file mode 100644 index 0000000000000..548c1fda86002 --- /dev/null +++ b/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 utility_PPC64LE_DWARF_Registers_h_ +#define 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 // utility_PPC64LE_DWARF_Registers_h_ diff --git a/lldb/source/Utility/PPC64LE_ehframe_Registers.h b/lldb/source/Utility/PPC64LE_ehframe_Registers.h new file mode 100644 index 0000000000000..77cb3e5924e76 --- /dev/null +++ b/lldb/source/Utility/PPC64LE_ehframe_Registers.h @@ -0,0 +1,193 @@ +//===-- PPC64LE_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 utility_PPC64LE_ehframe_Registers_h_ +#define utility_PPC64LE_ehframe_Registers_h_ + +// The register numbers used in the eh_frame unwind information. +// Should be the same as DWARF register numbers. + +namespace ppc64le_ehframe { + +enum { +  r0 = 0, +  r1, +  r2, +  r3, +  r4, +  r5, +  r6, +  r7, +  r8, +  r9, +  r10, +  r11, +  r12, +  r13, +  r14, +  r15, +  r16, +  r17, +  r18, +  r19, +  r20, +  r21, +  r22, +  r23, +  r24, +  r25, +  r26, +  r27, +  r28, +  r29, +  r30, +  r31, +  f0, +  f1, +  f2, +  f3, +  f4, +  f5, +  f6, +  f7, +  f8, +  f9, +  f10, +  f11, +  f12, +  f13, +  f14, +  f15, +  f16, +  f17, +  f18, +  f19, +  f20, +  f21, +  f22, +  f23, +  f24, +  f25, +  f26, +  f27, +  f28, +  f29, +  f30, +  f31, +  lr = 65, +  ctr, +  cr = 68, +  xer = 76, +  vr0, +  vr1, +  vr2, +  vr3, +  vr4, +  vr5, +  vr6, +  vr7, +  vr8, +  vr9, +  vr10, +  vr11, +  vr12, +  vr13, +  vr14, +  vr15, +  vr16, +  vr17, +  vr18, +  vr19, +  vr20, +  vr21, +  vr22, +  vr23, +  vr24, +  vr25, +  vr26, +  vr27, +  vr28, +  vr29, +  vr30, +  vr31, +  vscr = 110, +  vrsave = 117, +  pc, +  softe, +  trap, +  origr3, +  fpscr, +  msr, +  vs0, +  vs1, +  vs2, +  vs3, +  vs4, +  vs5, +  vs6, +  vs7, +  vs8, +  vs9, +  vs10, +  vs11, +  vs12, +  vs13, +  vs14, +  vs15, +  vs16, +  vs17, +  vs18, +  vs19, +  vs20, +  vs21, +  vs22, +  vs23, +  vs24, +  vs25, +  vs26, +  vs27, +  vs28, +  vs29, +  vs30, +  vs31, +  vs32, +  vs33, +  vs34, +  vs35, +  vs36, +  vs37, +  vs38, +  vs39, +  vs40, +  vs41, +  vs42, +  vs43, +  vs44, +  vs45, +  vs46, +  vs47, +  vs48, +  vs49, +  vs50, +  vs51, +  vs52, +  vs53, +  vs54, +  vs55, +  vs56, +  vs57, +  vs58, +  vs59, +  vs60, +  vs61, +  vs62, +  vs63, +}; +} + +#endif // utility_PPC64LE_ehframe_Registers_h_ diff --git a/lldb/source/Utility/PPC64_DWARF_Registers.h b/lldb/source/Utility/PPC64_DWARF_Registers.h new file mode 100644 index 0000000000000..6ba5b6ac37271 --- /dev/null +++ b/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 utility_PPC64_DWARF_Registers_h_ +#define 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 // utility_PPC64_DWARF_Registers_h_ diff --git a/lldb/source/Utility/ProcessInfo.cpp b/lldb/source/Utility/ProcessInfo.cpp new file mode 100644 index 0000000000000..5743d223be4fa --- /dev/null +++ b/lldb/source/Utility/ProcessInfo.cpp @@ -0,0 +1,333 @@ +//===-- ProcessInfo.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/ProcessInfo.h" + +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UserIDResolver.h" +#include "llvm/ADT/SmallString.h" + +#include <climits> + +using namespace lldb; +using namespace lldb_private; + +ProcessInfo::ProcessInfo() +    : m_executable(), m_arguments(), m_environment(), m_uid(UINT32_MAX), +      m_gid(UINT32_MAX), m_arch(), m_pid(LLDB_INVALID_PROCESS_ID) {} + +ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch, +                         lldb::pid_t pid) +    : m_executable(name), m_arguments(), m_environment(), m_uid(UINT32_MAX), +      m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {} + +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; +} + +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); +  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 = 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); +    } +  } +} + +void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const { +  if (m_pid != LLDB_INVALID_PROCESS_ID) +    s.Printf("    pid = %" PRIu64 "\n", m_pid); + +  if (m_parent_pid != LLDB_INVALID_PROCESS_ID) +    s.Printf(" parent = %" PRIu64 "\n", m_parent_pid); + +  if (m_executable) { +    s.Printf("   name = %s\n", m_executable.GetFilename().GetCString()); +    s.PutCString("   file = "); +    m_executable.Dump(&s); +    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); +    s.EOL(); +  } + +  if (UserIDIsValid()) { +    s.Format("    uid = {0,-5} ({1})\n", GetUserID(), +             resolver.GetUserName(GetUserID()).getValueOr("")); +  } +  if (GroupIDIsValid()) { +    s.Format("    gid = {0,-5} ({1})\n", GetGroupID(), +             resolver.GetGroupName(GetGroupID()).getValueOr("")); +  } +  if (EffectiveUserIDIsValid()) { +    s.Format("   euid = {0,-5} ({1})\n", GetEffectiveUserID(), +             resolver.GetUserName(GetEffectiveUserID()).getValueOr("")); +  } +  if (EffectiveGroupIDIsValid()) { +    s.Format("   egid = {0,-5} ({1})\n", GetEffectiveGroupID(), +             resolver.GetGroupName(GetEffectiveGroupID()).getValueOr("")); +  } +} + +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, m_parent_pid); + +    StreamString arch_strm; +    if (m_arch.IsValid()) +      m_arch.DumpTriple(arch_strm); + +    auto print = [&](bool (ProcessInstanceInfo::*isValid)() const, +                     uint32_t (ProcessInstanceInfo::*getID)() const, +                     llvm::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/lldb/source/Utility/RegisterValue.cpp b/lldb/source/Utility/RegisterValue.cpp new file mode 100644 index 0000000000000..a01c35a2818e4 --- /dev/null +++ b/lldb/source/Utility/RegisterValue.cpp @@ -0,0 +1,878 @@ +//===-- RegisterValue.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/RegisterValue.h" + +#include "lldb/Utility/Args.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 <assert.h> +#include <inttypes.h> +#include <stdio.h> + +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 *reg_info, void *dst, +                                        uint32_t dst_len, +                                        lldb::ByteOrder dst_byte_order, +                                        Status &error) const { +  if (reg_info == nullptr) { +    error.SetErrorString("invalid register info argument."); +    return 0; +  } + +  // 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; +  } + +  if (dst_len > kMaxRegisterByteSize) { +    error.SetErrorString("destination is too big"); +    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 *reg_info, +                                          const void *src, uint32_t src_len, +                                          lldb::ByteOrder src_byte_order, +                                          Status &error) { +  if (reg_info == nullptr) { +    error.SetErrorString("invalid register info argument."); +    return 0; +  } + +  // 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] +  if (src_len > kMaxRegisterByteSize) { +    error.SetErrorStringWithFormat( +        "register buffer is too small to receive %u bytes of data.", src_len); +    return 0; +  } + +  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: { +    switch (buffer.length) { +    default: +      break; +    case 1: +      scalar = *(const uint8_t *)buffer.bytes; +      return true; +    case 2: +      scalar = *reinterpret_cast<const uint16_t *>(buffer.bytes); +      return true; +    case 4: +      scalar = *reinterpret_cast<const uint32_t *>(buffer.bytes); +      return true; +    case 8: +      scalar = *reinterpret_cast<const uint64_t *>(buffer.bytes); +      return true; +    case 16: +    case 32: +    case 64: +      if (buffer.length % sizeof(uint64_t) == 0) { +        const auto length_in_bits = buffer.length * 8; +        const auto length_in_uint64 = buffer.length / sizeof(uint64_t); +        scalar = +            llvm::APInt(length_in_bits, +                        llvm::ArrayRef<uint64_t>( +                            reinterpret_cast<const uint64_t *>(buffer.bytes), +                            length_in_uint64)); +        return true; +      } +      break; +    } +  } 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 *reg_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)) +    SetValueFromData(reg_info, copy_data, 0, true); + +  return m_type; +} + +Status RegisterValue::SetValueFromData(const RegisterInfo *reg_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; + +  // Zero out the value in case we get partial data... +  memset(buffer.bytes, 0, sizeof(buffer.bytes)); + +  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.GetByteSize() == 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; +    buffer.length = reg_info->byte_size; +    buffer.byte_order = src.GetByteOrder(); +    assert(buffer.length <= kMaxRegisterByteSize); +    if (buffer.length > kMaxRegisterByteSize) +      buffer.length = kMaxRegisterByteSize; +    if (src.CopyByteOrderedData( +            src_offset,    // offset within "src" to start extracting data +            src_len,       // src length +            buffer.bytes,  // dst buffer +            buffer.length, // 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; +} + +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.SetErrorStringWithFormat( +          "'%s' is not a valid unsigned integer string value", +          value_str.str().c_str()); +      break; +    } + +    if (!Args::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.SetErrorStringWithFormat( +          "'%s' is not a valid signed integer string value", +          value_str.str().c_str()); +      break; +    } + +    if (!Args::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 = 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: +    assert(rhs.buffer.length <= kMaxRegisterByteSize); +    ::memcpy(buffer.bytes, rhs.buffer.bytes, kMaxRegisterByteSize); +    buffer.length = rhs.buffer.length; +    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.length) { +    default: +      break; +    case 1: +    case 2: +      return *reinterpret_cast<const uint16_t *>(buffer.bytes); +    } +  } 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.length) { +    default: +      break; +    case 1: +    case 2: +    case 4: +      return *reinterpret_cast<const uint32_t *>(buffer.bytes); +    } +  } 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.length) { +    default: +      break; +    case 1: +      return *(const uint8_t *)buffer.bytes; +    case 2: +      return *reinterpret_cast<const uint16_t *>(buffer.bytes); +    case 4: +      return *reinterpret_cast<const uint32_t *>(buffer.bytes); +    case 8: +      return *reinterpret_cast<const uint64_t *>(buffer.bytes); +    } +  } 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.length) { +    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))->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: +    return m_scalar.GetBytes(); +  case eTypeBytes: +    return buffer.bytes; +  } +  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.length; +  } +  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 this assertion fires off we need to increase the size of buffer.bytes, +  // or make it something that is allocated on the heap. Since the data buffer +  // is in a union, we can't make it a collection class like SmallVector... +  if (bytes && length > 0) { +    assert(length <= sizeof(buffer.bytes) && +           "Storing too many bytes in a RegisterValue."); +    m_type = eTypeBytes; +    buffer.length = length; +    memcpy(buffer.bytes, bytes, length); +    buffer.byte_order = byte_order; +  } else { +    m_type = eTypeInvalid; +    buffer.length = 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: +      if (buffer.length != rhs.buffer.length) +        return false; +      else { +        uint8_t length = buffer.length; +        if (length > kMaxRegisterByteSize) +          length = kMaxRegisterByteSize; +        return memcmp(buffer.bytes, rhs.buffer.bytes, length) == 0; +      } +      break; +    } +  } +  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.length - (bit / 8) - 1; +      else +        byte_idx = bit / 8; + +      const uint32_t byte_bit = bit % 8; +      if (byte_idx < buffer.length) { +        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.length - (bit / 8) - 1; +      else +        byte_idx = bit / 8; + +      const uint32_t byte_bit = bit % 8; +      if (byte_idx < buffer.length) { +        buffer.bytes[byte_idx] |= (1u << byte_bit); +        return true; +      } +    } +    break; +  } +  return false; +} diff --git a/lldb/source/Utility/RegularExpression.cpp b/lldb/source/Utility/RegularExpression.cpp new file mode 100644 index 0000000000000..fd9d963f7294c --- /dev/null +++ b/lldb/source/Utility/RegularExpression.cpp @@ -0,0 +1,41 @@ +//===-- RegularExpression.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/RegularExpression.h" + +#include <string> + +using namespace lldb_private; + +RegularExpression::RegularExpression(llvm::StringRef str) +    : m_regex_text(str), +      // m_regex does not reference str anymore after it is constructed. +      m_regex(llvm::Regex(str)) {} + +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>(llvm::inconvertibleErrorCode(), +                                               error); +  return llvm::Error::success(); +} diff --git a/lldb/source/Utility/Reproducer.cpp b/lldb/source/Utility/Reproducer.cpp new file mode 100644 index 0000000000000..4777d7576a321 --- /dev/null +++ b/lldb/source/Utility/Reproducer.cpp @@ -0,0 +1,354 @@ +//===-- Reproducer.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/Reproducer.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private; +using namespace lldb_private::repro; +using namespace llvm; +using namespace llvm::yaml; + +Reproducer &Reproducer::Instance() { return *InstanceImpl(); } + +llvm::Error Reproducer::Initialize(ReproducerMode mode, +                                   llvm::Optional<FileSpec> root) { +  lldbassert(!InstanceImpl() && "Already initialized."); +  InstanceImpl().emplace(); + +  switch (mode) { +  case ReproducerMode::Capture: { +    if (!root) { +      SmallString<128> repro_dir; +      auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir); +      if (ec) +        return make_error<StringError>( +            "unable to create unique reproducer directory", ec); +      root.emplace(repro_dir); +    } else { +      auto ec = sys::fs::create_directory(root->GetPath()); +      if (ec) +        return make_error<StringError>("unable to create reproducer directory", +                                       ec); +    } +    return Instance().SetCapture(root); +  } break; +  case ReproducerMode::Replay: +    return Instance().SetReplay(root); +  case ReproducerMode::Off: +    break; +  }; + +  return Error::success(); +} + +bool Reproducer::Initialized() { return InstanceImpl().operator bool(); } + +void Reproducer::Terminate() { +  lldbassert(InstanceImpl() && "Already terminated."); +  InstanceImpl().reset(); +} + +Optional<Reproducer> &Reproducer::InstanceImpl() { +  static Optional<Reproducer> g_reproducer; +  return g_reproducer; +} + +const Generator *Reproducer::GetGenerator() const { +  std::lock_guard<std::mutex> guard(m_mutex); +  if (m_generator) +    return &(*m_generator); +  return nullptr; +} + +const Loader *Reproducer::GetLoader() const { +  std::lock_guard<std::mutex> guard(m_mutex); +  if (m_loader) +    return &(*m_loader); +  return nullptr; +} + +Generator *Reproducer::GetGenerator() { +  std::lock_guard<std::mutex> guard(m_mutex); +  if (m_generator) +    return &(*m_generator); +  return nullptr; +} + +Loader *Reproducer::GetLoader() { +  std::lock_guard<std::mutex> guard(m_mutex); +  if (m_loader) +    return &(*m_loader); +  return nullptr; +} + +llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { +  std::lock_guard<std::mutex> guard(m_mutex); + +  if (root && m_loader) +    return make_error<StringError>( +        "cannot generate a reproducer when replay one", +        inconvertibleErrorCode()); + +  if (!root) { +    m_generator.reset(); +    return Error::success(); +  } + +  m_generator.emplace(*root); +  return Error::success(); +} + +llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) { +  std::lock_guard<std::mutex> guard(m_mutex); + +  if (root && m_generator) +    return make_error<StringError>( +        "cannot replay a reproducer when generating one", +        inconvertibleErrorCode()); + +  if (!root) { +    m_loader.reset(); +    return Error::success(); +  } + +  m_loader.emplace(*root); +  if (auto e = m_loader->LoadIndex()) +    return e; + +  return Error::success(); +} + +FileSpec Reproducer::GetReproducerPath() const { +  if (auto g = GetGenerator()) +    return g->GetRoot(); +  if (auto l = GetLoader()) +    return l->GetRoot(); +  return {}; +} + +static FileSpec MakeAbsolute(FileSpec file_spec) { +  SmallString<128> path; +  file_spec.GetPath(path, false); +  llvm::sys::fs::make_absolute(path); +  return FileSpec(path, file_spec.GetPathStyle()); +} + +Generator::Generator(FileSpec root) +    : m_root(MakeAbsolute(std::move(root))), m_done(false) { +  GetOrCreate<repro::WorkingDirectoryProvider>(); +} + +Generator::~Generator() {} + +ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { +  std::lock_guard<std::mutex> lock(m_providers_mutex); +  std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( +      provider->DynamicClassID(), std::move(provider)); +  auto e = m_providers.insert(std::move(key_value)); +  return e.first->getSecond().get(); +} + +void Generator::Keep() { +  assert(!m_done); +  m_done = true; + +  for (auto &provider : m_providers) +    provider.second->Keep(); + +  AddProvidersToIndex(); +} + +void Generator::Discard() { +  assert(!m_done); +  m_done = true; + +  for (auto &provider : m_providers) +    provider.second->Discard(); + +  llvm::sys::fs::remove_directories(m_root.GetPath()); +} + +const FileSpec &Generator::GetRoot() const { return m_root; } + +void Generator::AddProvidersToIndex() { +  FileSpec index = m_root; +  index.AppendPathComponent("index.yaml"); + +  std::error_code EC; +  auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC, +                                               sys::fs::OpenFlags::OF_None); +  yaml::Output yout(*strm); + +  std::vector<std::string> files; +  files.reserve(m_providers.size()); +  for (auto &provider : m_providers) { +    files.emplace_back(provider.second->GetFile()); +  } + +  yout << files; +} + +Loader::Loader(FileSpec root) +    : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {} + +llvm::Error Loader::LoadIndex() { +  if (m_loaded) +    return llvm::Error::success(); + +  FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); + +  auto error_or_file = MemoryBuffer::getFile(index.GetPath()); +  if (auto err = error_or_file.getError()) +    return make_error<StringError>("unable to load reproducer index", err); + +  yaml::Input yin((*error_or_file)->getBuffer()); +  yin >> m_files; +  if (auto err = yin.error()) +    return make_error<StringError>("unable to read reproducer index", err); + +  // Sort files to speed up search. +  llvm::sort(m_files); + +  // Remember that we've loaded the index. +  m_loaded = true; + +  return llvm::Error::success(); +} + +bool Loader::HasFile(StringRef file) { +  assert(m_loaded); +  auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); +  return (it != m_files.end()) && (*it == file); +} + +llvm::Expected<std::unique_ptr<DataRecorder>> +DataRecorder::Create(const FileSpec &filename) { +  std::error_code ec; +  auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec); +  if (ec) +    return llvm::errorCodeToError(ec); +  return std::move(recorder); +} + +DataRecorder *CommandProvider::GetNewDataRecorder() { +  std::size_t i = m_data_recorders.size() + 1; +  std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + +                          llvm::Twine(i) + llvm::Twine(".txt")) +                             .str(); +  auto recorder_or_error = +      DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); +  if (!recorder_or_error) { +    llvm::consumeError(recorder_or_error.takeError()); +    return nullptr; +  } + +  m_data_recorders.push_back(std::move(*recorder_or_error)); +  return m_data_recorders.back().get(); +} + +void CommandProvider::Keep() { +  std::vector<std::string> files; +  for (auto &recorder : m_data_recorders) { +    recorder->Stop(); +    files.push_back(recorder->GetFilename().GetPath()); +  } + +  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); +  std::error_code ec; +  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); +  if (ec) +    return; +  yaml::Output yout(os); +  yout << files; +} + +void CommandProvider::Discard() { m_data_recorders.clear(); } + +void VersionProvider::Keep() { +  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); +  std::error_code ec; +  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); +  if (ec) +    return; +  os << m_version << "\n"; +} + +void WorkingDirectoryProvider::Keep() { +  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); +  std::error_code ec; +  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); +  if (ec) +    return; +  os << m_cwd << "\n"; +} + +llvm::raw_ostream *ProcessGDBRemoteProvider::GetHistoryStream() { +  FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file); + +  std::error_code EC; +  m_stream_up = std::make_unique<raw_fd_ostream>(history_file.GetPath(), EC, +                                                 sys::fs::OpenFlags::OF_Text); +  return m_stream_up.get(); +} + +std::unique_ptr<CommandLoader> CommandLoader::Create(Loader *loader) { +  if (!loader) +    return {}; + +  FileSpec file = loader->GetFile<repro::CommandProvider::Info>(); +  if (!file) +    return {}; + +  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); +  if (auto err = error_or_file.getError()) +    return {}; + +  std::vector<std::string> files; +  llvm::yaml::Input yin((*error_or_file)->getBuffer()); +  yin >> files; + +  if (auto err = yin.error()) +    return {}; + +  for (auto &file : files) { +    FileSpec absolute_path = +        loader->GetRoot().CopyByAppendingPathComponent(file); +    file = absolute_path.GetPath(); +  } + +  return std::make_unique<CommandLoader>(std::move(files)); +} + +llvm::Optional<std::string> CommandLoader::GetNextFile() { +  if (m_index >= m_files.size()) +    return {}; +  return m_files[m_index++]; +} + +void ProviderBase::anchor() {} +char CommandProvider::ID = 0; +char FileProvider::ID = 0; +char ProcessGDBRemoteProvider::ID = 0; +char ProviderBase::ID = 0; +char VersionProvider::ID = 0; +char WorkingDirectoryProvider::ID = 0; +const char *CommandProvider::Info::file = "command-interpreter.yaml"; +const char *CommandProvider::Info::name = "command-interpreter"; +const char *FileProvider::Info::file = "files.yaml"; +const char *FileProvider::Info::name = "files"; +const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml"; +const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote"; +const char *VersionProvider::Info::file = "version.txt"; +const char *VersionProvider::Info::name = "version"; +const char *WorkingDirectoryProvider::Info::file = "cwd.txt"; +const char *WorkingDirectoryProvider::Info::name = "cwd"; diff --git a/lldb/source/Utility/ReproducerInstrumentation.cpp b/lldb/source/Utility/ReproducerInstrumentation.cpp new file mode 100644 index 0000000000000..473786ef4d3e3 --- /dev/null +++ b/lldb/source/Utility/ReproducerInstrumentation.cpp @@ -0,0 +1,122 @@ +//===-- ReproducerInstrumentation.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/ReproducerInstrumentation.h" +#include "lldb/Utility/Reproducer.h" + +using namespace lldb_private; +using namespace lldb_private::repro; + +void *IndexToObject::GetObjectForIndexImpl(unsigned idx) { +  return m_mapping.lookup(idx); +} + +void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) { +  assert(idx != 0 && "Cannot add object for sentinel"); +  m_mapping[idx] = object; +} + +template <> char *Deserializer::Deserialize<char *>() { +  return const_cast<char *>(Deserialize<const char *>()); +} + +template <> const char *Deserializer::Deserialize<const char *>() { +  auto pos = m_buffer.find('\0'); +  if (pos == llvm::StringRef::npos) +    return nullptr; +  const char *str = m_buffer.data(); +  m_buffer = m_buffer.drop_front(pos + 1); +  return str; +} + +bool Registry::Replay(const FileSpec &file) { +  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); +  if (auto err = error_or_file.getError()) +    return false; + +  return Replay((*error_or_file)->getBuffer()); +} + +bool Registry::Replay(llvm::StringRef buffer) { +#ifndef LLDB_REPRO_INSTR_TRACE +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API); +#endif + +  Deserializer deserializer(buffer); +  while (deserializer.HasData(1)) { +    unsigned id = deserializer.Deserialize<unsigned>(); + +#ifndef LLDB_REPRO_INSTR_TRACE +    LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id)); +#else +    llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n"; +#endif + +    GetReplayer(id)->operator()(deserializer); +  } + +  return true; +} + +void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer, +                          SignatureStr signature) { +  const unsigned id = m_replayers.size() + 1; +  assert(m_replayers.find(RunID) == m_replayers.end()); +  m_replayers[RunID] = std::make_pair(std::move(replayer), id); +  m_ids[id] = +      std::make_pair(m_replayers[RunID].first.get(), std::move(signature)); +} + +unsigned Registry::GetID(uintptr_t addr) { +  unsigned id = m_replayers[addr].second; +  assert(id != 0 && "Forgot to add function to registry?"); +  return id; +} + +std::string Registry::GetSignature(unsigned id) { +  assert(m_ids.count(id) != 0 && "ID not in registry"); +  return m_ids[id].second.ToString(); +} + +Replayer *Registry::GetReplayer(unsigned id) { +  assert(m_ids.count(id) != 0 && "ID not in registry"); +  return m_ids[id].first; +} + +std::string Registry::SignatureStr::ToString() const { +  return (result + (result.empty() ? "" : " ") + scope + "::" + name + args) +      .str(); +} + +unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) { +  unsigned index = m_mapping.size() + 1; +  auto it = m_mapping.find(object); +  if (it == m_mapping.end()) +    m_mapping[object] = index; +  return m_mapping[object]; +} + +Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args) +    : m_serializer(nullptr), m_pretty_func(pretty_func), +      m_pretty_args(pretty_args), m_local_boundary(false), +      m_result_recorded(true) { +  if (!g_global_boundary) { +    g_global_boundary = true; +    m_local_boundary = true; + +    LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})", +             m_pretty_func, m_pretty_args); +  } +} + +Recorder::~Recorder() { +  assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?"); +  UpdateBoundary(); +} + +bool lldb_private::repro::Recorder::g_global_boundary; diff --git a/lldb/source/Utility/Scalar.cpp b/lldb/source/Utility/Scalar.cpp new file mode 100644 index 0000000000000..e9aec17b6650d --- /dev/null +++ b/lldb/source/Utility/Scalar.cpp @@ -0,0 +1,2908 @@ +//===-- Scalar.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/Scalar.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/SmallString.h" + +#include <cinttypes> +#include <cstdio> + +using namespace lldb; +using namespace lldb_private; + +// Promote to max type currently follows the ANSI C rule for type promotion in +// expressions. +static Scalar::Type PromoteToMaxType( +    const Scalar &lhs,  // The const left hand side object +    const Scalar &rhs,  // The const right hand side object +    Scalar &temp_value, // A modifiable temp value than can be used to hold +                        // either the promoted lhs or rhs object +    const Scalar *&promoted_lhs_ptr, // Pointer to the resulting possibly +                                     // promoted value of lhs (at most one of +                                     // lhs/rhs will get promoted) +    const Scalar *&promoted_rhs_ptr  // Pointer to the resulting possibly +                                     // promoted value of rhs (at most one of +                                     // lhs/rhs will get promoted) +) { +  Scalar result; +  // Initialize the promoted values for both the right and left hand side +  // values to be the objects themselves. If no promotion is needed (both right +  // and left have the same type), then the temp_value will not get used. +  promoted_lhs_ptr = &lhs; +  promoted_rhs_ptr = &rhs; +  // Extract the types of both the right and left hand side values +  Scalar::Type lhs_type = lhs.GetType(); +  Scalar::Type rhs_type = rhs.GetType(); + +  if (lhs_type > rhs_type) { +    // Right hand side need to be promoted +    temp_value = rhs; // Copy right hand side into the temp value +    if (temp_value.Promote(lhs_type)) // Promote it +      promoted_rhs_ptr = +          &temp_value; // Update the pointer for the promoted right hand side +  } else if (lhs_type < rhs_type) { +    // Left hand side need to be promoted +    temp_value = lhs; // Copy left hand side value into the temp value +    if (temp_value.Promote(rhs_type)) // Promote it +      promoted_lhs_ptr = +          &temp_value; // Update the pointer for the promoted left hand side +  } + +  // Make sure our type promotion worked as expected +  if (promoted_lhs_ptr->GetType() == promoted_rhs_ptr->GetType()) +    return promoted_lhs_ptr->GetType(); // Return the resulting max type + +  // Return the void type (zero) if we fail to promote either of the values. +  return Scalar::e_void; +} + +Scalar::Scalar() : m_type(e_void), m_float(static_cast<float>(0)) {} + +bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const { +  size_t byte_size = GetByteSize(); +  if (byte_size > 0) { +    const uint8_t *bytes = reinterpret_cast<const uint8_t *>(GetBytes()); + +    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 +        bytes += byte_size - limit_byte_size; +        byte_size = limit_byte_size; +      } +    } + +    data.SetData(bytes, byte_size, endian::InlHostByteOrder()); +    return true; +  } +  data.Clear(); +  return false; +} + +const void *Scalar::GetBytes() const { +  const uint64_t *apint_words; +  const uint8_t *bytes; +  static float_t flt_val; +  static double_t dbl_val; +  static uint64_t swapped_words[8]; +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +    bytes = reinterpret_cast<const uint8_t *>(m_integer.getRawData()); +    // getRawData always returns a pointer to an uint64_t.  If we have a +    // smaller type, we need to update the pointer on big-endian systems. +    if (endian::InlHostByteOrder() == eByteOrderBig) { +      size_t byte_size = m_integer.getBitWidth() / 8; +      if (byte_size < 8) +        bytes += 8 - byte_size; +    } +    return bytes; +  // getRawData always returns a pointer to an array of uint64_t values, +  // where the least-significant word always comes first.  On big-endian +  // systems we need to swap the words. +  case e_sint128: +  case e_uint128: +    apint_words = m_integer.getRawData(); +    if (endian::InlHostByteOrder() == eByteOrderBig) { +      swapped_words[0] = apint_words[1]; +      swapped_words[1] = apint_words[0]; +      apint_words = swapped_words; +    } +    return reinterpret_cast<const void *>(apint_words); +  case e_sint256: +  case e_uint256: +    apint_words = m_integer.getRawData(); +    if (endian::InlHostByteOrder() == eByteOrderBig) { +      swapped_words[0] = apint_words[3]; +      swapped_words[1] = apint_words[2]; +      swapped_words[2] = apint_words[1]; +      swapped_words[3] = apint_words[0]; +      apint_words = swapped_words; +    } +    return reinterpret_cast<const void *>(apint_words); +  case e_sint512: +  case e_uint512: +    apint_words = m_integer.getRawData(); +    if (endian::InlHostByteOrder() == eByteOrderBig) { +      swapped_words[0] = apint_words[7]; +      swapped_words[1] = apint_words[6]; +      swapped_words[2] = apint_words[5]; +      swapped_words[3] = apint_words[4]; +      swapped_words[4] = apint_words[3]; +      swapped_words[5] = apint_words[2]; +      swapped_words[6] = apint_words[1]; +      swapped_words[7] = apint_words[0]; +      apint_words = swapped_words; +    } +    return reinterpret_cast<const void *>(apint_words); +  case e_float: +    flt_val = m_float.convertToFloat(); +    return reinterpret_cast<const void *>(&flt_val); +  case e_double: +    dbl_val = m_float.convertToDouble(); +    return reinterpret_cast<const void *>(&dbl_val); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    apint_words = ldbl_val.getRawData(); +    // getRawData always returns a pointer to an array of two uint64_t values, +    // where the least-significant word always comes first.  On big-endian +    // systems we need to swap the two words. +    if (endian::InlHostByteOrder() == eByteOrderBig) { +      swapped_words[0] = apint_words[1]; +      swapped_words[1] = apint_words[0]; +      apint_words = swapped_words; +    } +    return reinterpret_cast<const void *>(apint_words); +  } +  return nullptr; +} + +size_t Scalar::GetByteSize() const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return (m_integer.getBitWidth() / 8); +  case e_float: +    return sizeof(float_t); +  case e_double: +    return sizeof(double_t); +  case e_long_double: +    return sizeof(long_double_t); +  } +  return 0; +} + +bool Scalar::IsZero() const { +  llvm::APInt zero_int = llvm::APInt::getNullValue(m_integer.getBitWidth() / 8); +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_uint512: +  case e_sint512: +    return llvm::APInt::isSameValue(zero_int, m_integer); +  case e_float: +  case e_double: +  case e_long_double: +    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_sint: +  case e_slong: +  case e_slonglong: +  case e_sint128: +  case e_sint256: +  case e_sint512: +    s->PutCString(m_integer.toString(10, true)); +    break; +  case e_uint: +  case e_ulong: +  case e_ulonglong: +  case e_uint128: +  case e_uint256: +  case e_uint512: +    s->PutCString(m_integer.toString(10, false)); +    break; +  case e_float: +  case e_double: +  case e_long_double: +    llvm::SmallString<24> string; +    m_float.toString(string); +    s->Printf("%s", string.c_str()); +    break; +  } +} + +const char *Scalar::GetTypeAsCString() const { +  switch (m_type) { +  case e_void: +    return "void"; +  case e_sint: +    return "int"; +  case e_uint: +    return "unsigned int"; +  case e_slong: +    return "long"; +  case e_ulong: +    return "unsigned long"; +  case e_slonglong: +    return "long long"; +  case e_ulonglong: +    return "unsigned long long"; +  case e_sint128: +    return "int128_t"; +  case e_uint128: +    return "unsigned int128_t"; +  case e_sint256: +    return "int256_t"; +  case e_uint256: +    return "unsigned int256_t"; +  case e_sint512: +    return "int512_t"; +  case e_uint512: +    return "unsigned int512_t"; +  case e_float: +    return "float"; +  case e_double: +    return "double"; +  case e_long_double: +    return "long double"; +  } +  return "<invalid Scalar type>"; +} + +Scalar &Scalar::operator=(const Scalar &rhs) { +  if (this != &rhs) { +    m_type = rhs.m_type; +    m_integer = llvm::APInt(rhs.m_integer); +    m_float = rhs.m_float; +  } +  return *this; +} + +Scalar &Scalar::operator=(const int v) { +  m_type = e_sint; +  m_integer = llvm::APInt(sizeof(int) * 8, v, true); +  return *this; +} + +Scalar &Scalar::operator=(unsigned int v) { +  m_type = e_uint; +  m_integer = llvm::APInt(sizeof(int) * 8, v); +  return *this; +} + +Scalar &Scalar::operator=(long v) { +  m_type = e_slong; +  m_integer = llvm::APInt(sizeof(long) * 8, v, true); +  return *this; +} + +Scalar &Scalar::operator=(unsigned long v) { +  m_type = e_ulong; +  m_integer = llvm::APInt(sizeof(long) * 8, v); +  return *this; +} + +Scalar &Scalar::operator=(long long v) { +  m_type = e_slonglong; +  m_integer = llvm::APInt(sizeof(long) * 8, v, true); +  return *this; +} + +Scalar &Scalar::operator=(unsigned long long v) { +  m_type = e_ulonglong; +  m_integer = llvm::APInt(sizeof(long long) * 8, v); +  return *this; +} + +Scalar &Scalar::operator=(float v) { +  m_type = e_float; +  m_float = llvm::APFloat(v); +  return *this; +} + +Scalar &Scalar::operator=(double v) { +  m_type = e_double; +  m_float = llvm::APFloat(v); +  return *this; +} + +Scalar &Scalar::operator=(long double v) { +  m_type = e_long_double; +  if (m_ieee_quad) +    m_float = llvm::APFloat(llvm::APFloat::IEEEquad(), +                            llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, +                                        (reinterpret_cast<type128 *>(&v))->x)); +  else +    m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(), +                            llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, +                                        (reinterpret_cast<type128 *>(&v))->x)); +  return *this; +} + +Scalar &Scalar::operator=(llvm::APInt rhs) { +  m_integer = llvm::APInt(rhs); +  switch (m_integer.getBitWidth()) { +  case 8: +  case 16: +  case 32: +    if (m_integer.isSignedIntN(sizeof(sint_t) * 8)) +      m_type = e_sint; +    else +      m_type = e_uint; +    break; +  case 64: +    if (m_integer.isSignedIntN(sizeof(slonglong_t) * 8)) +      m_type = e_slonglong; +    else +      m_type = e_ulonglong; +    break; +  case 128: +    if (m_integer.isSignedIntN(BITWIDTH_INT128)) +      m_type = e_sint128; +    else +      m_type = e_uint128; +    break; +  case 256: +    if (m_integer.isSignedIntN(BITWIDTH_INT256)) +      m_type = e_sint256; +    else +      m_type = e_uint256; +    break; +  case 512: +    if (m_integer.isSignedIntN(BITWIDTH_INT512)) +      m_type = e_sint512; +    else +      m_type = e_uint512; +    break; +  } +  return *this; +} + +Scalar::~Scalar() = default; + +Scalar::Type Scalar::GetBestTypeForBitSize(size_t bit_size, bool sign) { +  // Scalar types are always host types, hence the sizeof(). +  if (sign) { +    if (bit_size <= sizeof(int)*8) return Scalar::e_sint; +    if (bit_size <= sizeof(long)*8) return Scalar::e_slong; +    if (bit_size <= sizeof(long long)*8) return Scalar::e_slonglong; +    if (bit_size <= 128) return Scalar::e_sint128; +    if (bit_size <= 256) return Scalar::e_sint256; +    if (bit_size <= 512) return Scalar::e_sint512; +  } else { +    if (bit_size <= sizeof(unsigned int)*8) return Scalar::e_uint; +    if (bit_size <= sizeof(unsigned long)*8) return Scalar::e_ulong; +    if (bit_size <= sizeof(unsigned long long)*8) return Scalar::e_ulonglong; +    if (bit_size <= 128) return Scalar::e_uint128; +    if (bit_size <= 256) return Scalar::e_uint256; +    if (bit_size <= 512) return Scalar::e_uint512; +  } +  return Scalar::e_void; +} + +void Scalar::TruncOrExtendTo(Scalar::Type type, uint16_t bits) { +  switch (type) { +  case e_sint: +  case e_slong: +  case e_slonglong: +  case e_sint128: +  case e_sint256: +  case e_sint512: +    m_integer = m_integer.sextOrTrunc(bits); +    break; +  case e_uint: +  case e_ulong: +  case e_ulonglong: +  case e_uint128: +  case e_uint256: +  case e_uint512: +    m_integer = m_integer.zextOrTrunc(bits); +    break; +  default: +    llvm_unreachable("Promoting a Scalar to a specific number of bits is only " +                     "supported for integer types."); +  } +  m_type = type; +} + +bool Scalar::Promote(Scalar::Type type) { +  bool success = false; +  switch (m_type) { +  case e_void: +    break; + +  case e_sint: +    switch (type) { +    case e_void: +      break; +    case e_sint: +      success = true; +      break; +    case e_uint: +      m_integer = m_integer.sextOrTrunc(sizeof(uint_t) * 8); +      success = true; +      break; + +    case e_slong: +      m_integer = m_integer.sextOrTrunc(sizeof(slong_t) * 8); +      success = true; +      break; + +    case e_ulong: +      m_integer = m_integer.sextOrTrunc(sizeof(ulong_t) * 8); +      success = true; +      break; + +    case e_slonglong: +      m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8); +      success = true; +      break; + +    case e_ulonglong: +      m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8); +      success = true; +      break; + +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_uint: +    switch (type) { +    case e_void: +    case e_sint: +      break; +    case e_uint: +      success = true; +      break; +    case e_slong: +      m_integer = m_integer.zextOrTrunc(sizeof(slong_t) * 8); +      success = true; +      break; + +    case e_ulong: +      m_integer = m_integer.zextOrTrunc(sizeof(ulong_t) * 8); +      success = true; +      break; + +    case e_slonglong: +      m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8); +      success = true; +      break; + +    case e_ulonglong: +      m_integer = m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8); +      success = true; +      break; + +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_slong: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +      break; +    case e_slong: +      success = true; +      break; +    case e_ulong: +      m_integer = m_integer.sextOrTrunc(sizeof(ulong_t) * 8); +      success = true; +      break; + +    case e_slonglong: +      m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8); +      success = true; +      break; + +    case e_ulonglong: +      m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8); +      success = true; +      break; + +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_ulong: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +      break; +    case e_ulong: +      success = true; +      break; +    case e_slonglong: +      m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8); +      success = true; +      break; + +    case e_ulonglong: +      m_integer = m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8); +      success = true; +      break; + +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_slonglong: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +      break; +    case e_slonglong: +      success = true; +      break; +    case e_ulonglong: +      m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8); +      success = true; +      break; + +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_ulonglong: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +      break; +    case e_ulonglong: +      success = true; +      break; +    case e_sint128: +    case e_uint128: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_sint128: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +      break; +    case e_sint128: +      success = true; +      break; +    case e_uint128: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128); +      success = true; +      break; + +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_uint128: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +      break; +    case e_uint128: +      success = true; +      break; +    case e_sint256: +    case e_uint256: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_sint256: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +      break; +    case e_sint256: +      success = true; +      break; +    case e_uint256: +      m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256); +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, true, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_uint256: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +      break; +    case e_uint256: +      success = true; +      break; + +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512); +      success = true; +      break; + +    case e_float: +      m_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_double: +      m_float = llvm::APFloat(llvm::APFloat::IEEEdouble()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; + +    case e_long_double: +      m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                          : llvm::APFloat::x87DoubleExtended()); +      m_float.convertFromAPInt(m_integer, false, +                               llvm::APFloat::rmNearestTiesToEven); +      success = true; +      break; +    } +    break; + +  case e_sint512: +  case e_uint512: +    lldbassert(false && "unimplemented"); +    break; + +  case e_float: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_uint512: +    case e_sint512: +      break; +    case e_float: +      success = true; +      break; +    case e_double: +      m_float = llvm::APFloat(static_cast<double_t>(m_float.convertToFloat())); +      success = true; +      break; + +    case e_long_double: { +      bool ignore; +      m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                  : llvm::APFloat::x87DoubleExtended(), +                      llvm::APFloat::rmNearestTiesToEven, &ignore); +      success = true; +      break; +    } +    } +    break; + +  case e_double: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +    case e_float: +      break; +    case e_double: +      success = true; +      break; +    case e_long_double: { +      bool ignore; +      m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad() +                                  : llvm::APFloat::x87DoubleExtended(), +                      llvm::APFloat::rmNearestTiesToEven, &ignore); +      success = true; +      break; +    } +    } +    break; + +  case e_long_double: +    switch (type) { +    case e_void: +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +    case e_float: +    case e_double: +      break; +    case e_long_double: +      success = true; +      break; +    } +    break; +  } + +  if (success) +    m_type = type; +  return success; +} + +const char *Scalar::GetValueTypeAsCString(Scalar::Type type) { +  switch (type) { +  case e_void: +    return "void"; +  case e_sint: +    return "int"; +  case e_uint: +    return "unsigned int"; +  case e_slong: +    return "long"; +  case e_ulong: +    return "unsigned long"; +  case e_slonglong: +    return "long long"; +  case e_ulonglong: +    return "unsigned long long"; +  case e_float: +    return "float"; +  case e_double: +    return "double"; +  case e_long_double: +    return "long double"; +  case e_sint128: +    return "int128_t"; +  case e_uint128: +    return "uint128_t"; +  case e_sint256: +    return "int256_t"; +  case e_uint256: +    return "uint256_t"; +  case e_sint512: +    return "int512_t"; +  case e_uint512: +    return "uint512_t"; +  } +  return "???"; +} + +Scalar::Type +Scalar::GetValueTypeForSignedIntegerWithByteSize(size_t byte_size) { +  if (byte_size <= sizeof(sint_t)) +    return e_sint; +  if (byte_size <= sizeof(slong_t)) +    return e_slong; +  if (byte_size <= sizeof(slonglong_t)) +    return e_slonglong; +  return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForUnsignedIntegerWithByteSize(size_t byte_size) { +  if (byte_size <= sizeof(uint_t)) +    return e_uint; +  if (byte_size <= sizeof(ulong_t)) +    return e_ulong; +  if (byte_size <= sizeof(ulonglong_t)) +    return e_ulonglong; +  return e_void; +} + +Scalar::Type Scalar::GetValueTypeForFloatWithByteSize(size_t byte_size) { +  if (byte_size == sizeof(float_t)) +    return e_float; +  if (byte_size == sizeof(double_t)) +    return e_double; +  if (byte_size == sizeof(long_double_t)) +    return e_long_double; +  return e_void; +} + +bool Scalar::MakeSigned() { +  bool success = false; + +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +    success = true; +    break; +  case e_uint: +    m_type = e_sint; +    success = true; +    break; +  case e_slong: +    success = true; +    break; +  case e_ulong: +    m_type = e_slong; +    success = true; +    break; +  case e_slonglong: +    success = true; +    break; +  case e_ulonglong: +    m_type = e_slonglong; +    success = true; +    break; +  case e_sint128: +    success = true; +    break; +  case e_uint128: +    m_type = e_sint128; +    success = true; +    break; +  case e_sint256: +    success = true; +    break; +  case e_uint256: +    m_type = e_sint256; +    success = true; +    break; +  case e_sint512: +    success = true; +    break; +  case e_uint512: +    m_type = e_sint512; +    success = true; +    break; +  case e_float: +    success = true; +    break; +  case e_double: +    success = true; +    break; +  case e_long_double: +    success = true; +    break; +  } + +  return success; +} + +bool Scalar::MakeUnsigned() { +  bool success = false; + +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +    m_type = e_uint; +    success = true; +    break; +  case e_uint: +    success = true; +    break; +  case e_slong: +    m_type = e_ulong; +    success = true; +    break; +  case e_ulong: +    success = true; +    break; +  case e_slonglong: +    m_type = e_ulonglong; +    success = true; +    break; +  case e_ulonglong: +    success = true; +    break; +  case e_sint128: +    m_type = e_uint128; +    success = true; +    break; +  case e_uint128: +    success = true; +    break; +  case e_sint256: +    m_type = e_uint256; +    success = true; +    break; +  case e_uint256: +    success = true; +    break; +  case e_sint512: +    m_type = e_uint512; +    success = true; +    break; +  case e_uint512: +    success = true; +    break; +  case e_float: +    success = true; +    break; +  case e_double: +    success = true; +    break; +  case e_long_double: +    success = true; +    break; +  } + +  return success; +} + +signed char Scalar::SChar(char fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<schar_t>( +        (m_integer.sextOrTrunc(sizeof(schar_t) * 8)).getSExtValue()); +  case e_float: +    return static_cast<schar_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<schar_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<schar_t>( +        (ldbl_val.sextOrTrunc(sizeof(schar_t) * 8)).getSExtValue()); +  } +  return fail_value; +} + +unsigned char Scalar::UChar(unsigned char fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<uchar_t>( +        (m_integer.zextOrTrunc(sizeof(uchar_t) * 8)).getZExtValue()); +  case e_float: +    return static_cast<uchar_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<uchar_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<uchar_t>( +        (ldbl_val.zextOrTrunc(sizeof(uchar_t) * 8)).getZExtValue()); +  } +  return fail_value; +} + +short Scalar::SShort(short fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<sshort_t>( +        (m_integer.sextOrTrunc(sizeof(sshort_t) * 8)).getSExtValue()); +  case e_float: +    return static_cast<sshort_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<sshort_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<sshort_t>( +        (ldbl_val.sextOrTrunc(sizeof(sshort_t) * 8)).getSExtValue()); +  } +  return fail_value; +} + +unsigned short Scalar::UShort(unsigned short fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<ushort_t>( +        (m_integer.zextOrTrunc(sizeof(ushort_t) * 8)).getZExtValue()); +  case e_float: +    return static_cast<ushort_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<ushort_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<ushort_t>( +        (ldbl_val.zextOrTrunc(sizeof(ushort_t) * 8)).getZExtValue()); +  } +  return fail_value; +} + +int Scalar::SInt(int fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<sint_t>( +        (m_integer.sextOrTrunc(sizeof(sint_t) * 8)).getSExtValue()); +  case e_float: +    return static_cast<sint_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<sint_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<sint_t>( +        (ldbl_val.sextOrTrunc(sizeof(sint_t) * 8)).getSExtValue()); +  } +  return fail_value; +} + +unsigned int Scalar::UInt(unsigned int fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<uint_t>( +        (m_integer.zextOrTrunc(sizeof(uint_t) * 8)).getZExtValue()); +  case e_float: +    return static_cast<uint_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<uint_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<uint_t>( +        (ldbl_val.zextOrTrunc(sizeof(uint_t) * 8)).getZExtValue()); +  } +  return fail_value; +} + +long Scalar::SLong(long fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<slong_t>( +        (m_integer.sextOrTrunc(sizeof(slong_t) * 8)).getSExtValue()); +  case e_float: +    return static_cast<slong_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<slong_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<slong_t>( +        (ldbl_val.sextOrTrunc(sizeof(slong_t) * 8)).getSExtValue()); +  } +  return fail_value; +} + +unsigned long Scalar::ULong(unsigned long fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<ulong_t>( +        (m_integer.zextOrTrunc(sizeof(ulong_t) * 8)).getZExtValue()); +  case e_float: +    return static_cast<ulong_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<ulong_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<ulong_t>( +        (ldbl_val.zextOrTrunc(sizeof(ulong_t) * 8)).getZExtValue()); +  } +  return fail_value; +} + +long long Scalar::SLongLong(long long fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<slonglong_t>( +        (m_integer.sextOrTrunc(sizeof(slonglong_t) * 8)).getSExtValue()); +  case e_float: +    return static_cast<slonglong_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<slonglong_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<slonglong_t>( +        (ldbl_val.sextOrTrunc(sizeof(slonglong_t) * 8)).getSExtValue()); +  } +  return fail_value; +} + +unsigned long long Scalar::ULongLong(unsigned long long fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<ulonglong_t>( +        (m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue()); +  case e_float: +    return static_cast<ulonglong_t>(m_float.convertToFloat()); +  case e_double: { +    double d_val = m_float.convertToDouble(); +    llvm::APInt rounded_double = +        llvm::APIntOps::RoundDoubleToAPInt(d_val, sizeof(ulonglong_t) * 8); +    return static_cast<ulonglong_t>( +        (rounded_double.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue()); +  } +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<ulonglong_t>( +        (ldbl_val.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue()); +  } +  return fail_value; +} + +llvm::APInt Scalar::SInt128(llvm::APInt &fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return m_integer; +  case e_float: +  case e_double: +  case e_long_double: +    return m_float.bitcastToAPInt(); +  } +  return fail_value; +} + +llvm::APInt Scalar::UInt128(const llvm::APInt &fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return m_integer; +  case e_float: +  case e_double: +  case e_long_double: +    return m_float.bitcastToAPInt(); +  } +  return fail_value; +} + +float Scalar::Float(float fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return llvm::APIntOps::RoundAPIntToFloat(m_integer); +  case e_float: +    return m_float.convertToFloat(); +  case e_double: +    return static_cast<float_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return ldbl_val.bitsToFloat(); +  } +  return fail_value; +} + +double Scalar::Double(double fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return llvm::APIntOps::RoundAPIntToDouble(m_integer); +  case e_float: +    return static_cast<double_t>(m_float.convertToFloat()); +  case e_double: +    return m_float.convertToDouble(); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return ldbl_val.bitsToFloat(); +  } +  return fail_value; +} + +long double Scalar::LongDouble(long double fail_value) const { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    return static_cast<long_double_t>( +        llvm::APIntOps::RoundAPIntToDouble(m_integer)); +  case e_float: +    return static_cast<long_double_t>(m_float.convertToFloat()); +  case e_double: +    return static_cast<long_double_t>(m_float.convertToDouble()); +  case e_long_double: +    llvm::APInt ldbl_val = m_float.bitcastToAPInt(); +    return static_cast<long_double_t>(ldbl_val.bitsToDouble()); +  } +  return fail_value; +} + +Scalar &Scalar::operator+=(const Scalar &rhs) { +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((m_type = PromoteToMaxType(*this, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (m_type) { +    case e_void: +      break; +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +      m_integer = a->m_integer + b->m_integer; +      break; + +    case e_float: +    case e_double: +    case e_long_double: +      m_float = a->m_float + b->m_float; +      break; +    } +  } +  return *this; +} + +Scalar &Scalar::operator<<=(const Scalar &rhs) { +  switch (m_type) { +  case e_void: +  case e_float: +  case e_double: +  case e_long_double: +    m_type = e_void; +    break; + +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    switch (rhs.m_type) { +    case e_void: +    case e_float: +    case e_double: +    case e_long_double: +      m_type = e_void; +      break; +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +      m_integer = m_integer << rhs.m_integer; +      break; +    } +    break; +  } +  return *this; +} + +bool Scalar::ShiftRightLogical(const Scalar &rhs) { +  switch (m_type) { +  case e_void: +  case e_float: +  case e_double: +  case e_long_double: +    m_type = e_void; +    break; + +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    switch (rhs.m_type) { +    case e_void: +    case e_float: +    case e_double: +    case e_long_double: +      m_type = e_void; +      break; +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.lshr(rhs.m_integer); +      break; +    } +    break; +  } +  return m_type != e_void; +} + +Scalar &Scalar::operator>>=(const Scalar &rhs) { +  switch (m_type) { +  case e_void: +  case e_float: +  case e_double: +  case e_long_double: +    m_type = e_void; +    break; + +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    switch (rhs.m_type) { +    case e_void: +    case e_float: +    case e_double: +    case e_long_double: +      m_type = e_void; +      break; +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +      m_integer = m_integer.ashr(rhs.m_integer); +      break; +    } +    break; +  } +  return *this; +} + +Scalar &Scalar::operator&=(const Scalar &rhs) { +  switch (m_type) { +  case e_void: +  case e_float: +  case e_double: +  case e_long_double: +    m_type = e_void; +    break; + +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    switch (rhs.m_type) { +    case e_void: +    case e_float: +    case e_double: +    case e_long_double: +      m_type = e_void; +      break; +    case e_sint: +    case e_uint: +    case e_slong: +    case e_ulong: +    case e_slonglong: +    case e_ulonglong: +    case e_sint128: +    case e_uint128: +    case e_sint256: +    case e_uint256: +    case e_sint512: +    case e_uint512: +      m_integer &= rhs.m_integer; +      break; +    } +    break; +  } +  return *this; +} + +bool Scalar::AbsoluteValue() { +  switch (m_type) { +  case e_void: +    break; + +  case e_sint: +  case e_slong: +  case e_slonglong: +  case e_sint128: +  case e_sint256: +  case e_sint512: +    if (m_integer.isNegative()) +      m_integer = -m_integer; +    return true; + +  case e_uint: +  case e_ulong: +  case e_ulonglong: +    return true; +  case e_uint128: +  case e_uint256: +  case e_uint512: +  case e_float: +  case e_double: +  case e_long_double: +    m_float.clearSign(); +    return true; +  } +  return false; +} + +bool Scalar::UnaryNegate() { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    m_integer = -m_integer; +    return true; +  case e_float: +  case e_double: +  case e_long_double: +    m_float.changeSign(); +    return true; +  } +  return false; +} + +bool Scalar::OnesComplement() { +  switch (m_type) { +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    m_integer = ~m_integer; +    return true; + +  case e_void: +  case e_float: +  case e_double: +  case e_long_double: +    break; +  } +  return false; +} + +const Scalar lldb_private::operator+(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_void: +      break; +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer + b->m_integer; +      break; +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      result.m_float = a->m_float + b->m_float; +      break; +    } +  } +  return result; +} + +const Scalar lldb_private::operator-(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_void: +      break; +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer - b->m_integer; +      break; +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      result.m_float = a->m_float - b->m_float; +      break; +    } +  } +  return result; +} + +const Scalar lldb_private::operator/(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_void: +      break; +    case Scalar::e_sint: +    case Scalar::e_slong: +    case Scalar::e_slonglong: +    case Scalar::e_sint128: +    case Scalar::e_sint256: +    case Scalar::e_sint512: +      if (b->m_integer != 0) { +        result.m_integer = a->m_integer.sdiv(b->m_integer); +        return result; +      } +      break; +    case Scalar::e_uint: +    case Scalar::e_ulong: +    case Scalar::e_ulonglong: +    case Scalar::e_uint128: +    case Scalar::e_uint256: +    case Scalar::e_uint512: +      if (b->m_integer != 0) { +        result.m_integer = a->m_integer.udiv(b->m_integer); +        return result; +      } +      break; +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      if (!b->m_float.isZero()) { +        result.m_float = a->m_float / b->m_float; +        return result; +      } +      break; +    } +  } +  // 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*(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_void: +      break; +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer * b->m_integer; +      break; +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      result.m_float = a->m_float * b->m_float; +      break; +    } +  } +  return result; +} + +const Scalar lldb_private::operator&(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer & b->m_integer; +      break; +    case Scalar::e_void: +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      // No bitwise AND on floats, doubles of long doubles +      result.m_type = Scalar::e_void; +      break; +    } +  } +  return result; +} + +const Scalar lldb_private::operator|(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer | b->m_integer; +      break; + +    case Scalar::e_void: +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      // No bitwise AND on floats, doubles of long doubles +      result.m_type = Scalar::e_void; +      break; +    } +  } +  return result; +} + +const Scalar lldb_private::operator%(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    default: +      break; +    case Scalar::e_void: +      break; +    case Scalar::e_sint: +    case Scalar::e_slong: +    case Scalar::e_slonglong: +    case Scalar::e_sint128: +    case Scalar::e_sint256: +    case Scalar::e_sint512: +      if (b->m_integer != 0) { +        result.m_integer = a->m_integer.srem(b->m_integer); +        return result; +      } +      break; +    case Scalar::e_uint: +    case Scalar::e_ulong: +    case Scalar::e_ulonglong: +    case Scalar::e_uint128: +    case Scalar::e_uint256: +    case Scalar::e_uint512: +      if (b->m_integer != 0) { +        result.m_integer = a->m_integer.urem(b->m_integer); +        return result; +      } +      break; +    } +  } +  result.m_type = Scalar::e_void; +  return result; +} + +const Scalar lldb_private::operator^(const Scalar &lhs, const Scalar &rhs) { +  Scalar result; +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != +      Scalar::e_void) { +    switch (result.m_type) { +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      result.m_integer = a->m_integer ^ b->m_integer; +      break; + +    case Scalar::e_void: +    case Scalar::e_float: +    case Scalar::e_double: +    case Scalar::e_long_double: +      // No bitwise AND on floats, doubles of long doubles +      result.m_type = Scalar::e_void; +      break; +    } +  } +  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 eEncodingUint: +    if (byte_size <= sizeof(uint64_t)) { +      uint64_t uval64; +      if (!llvm::to_integer(value_str, uval64)) +        error.SetErrorStringWithFormat( +            "'%s' is not a valid unsigned integer string value", value_str); +      else if (!UIntValueIsValidForSize(uval64, byte_size)) +        error.SetErrorStringWithFormat( +            "value 0x%" PRIx64 " is too large to fit in a %" PRIu64 +            " byte unsigned integer value", +            uval64, static_cast<uint64_t>(byte_size)); +      else { +        m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize(byte_size); +        switch (m_type) { +        case e_uint: +          m_integer = llvm::APInt(sizeof(uint_t) * 8, uval64, false); +          break; +        case e_ulong: +          m_integer = llvm::APInt(sizeof(ulong_t) * 8, uval64, false); +          break; +        case e_ulonglong: +          m_integer = llvm::APInt(sizeof(ulonglong_t) * 8, uval64, false); +          break; +        default: +          error.SetErrorStringWithFormat( +              "unsupported unsigned integer byte size: %" PRIu64 "", +              static_cast<uint64_t>(byte_size)); +          break; +        } +      } +    } else { +      error.SetErrorStringWithFormat( +          "unsupported unsigned integer byte size: %" PRIu64 "", +          static_cast<uint64_t>(byte_size)); +      return error; +    } +    break; + +  case eEncodingSint: +    if (byte_size <= sizeof(int64_t)) { +      int64_t sval64; +      if (!llvm::to_integer(value_str, sval64)) +        error.SetErrorStringWithFormat( +            "'%s' is not a valid signed integer string value", value_str); +      else if (!SIntValueIsValidForSize(sval64, byte_size)) +        error.SetErrorStringWithFormat( +            "value 0x%" PRIx64 " is too large to fit in a %" PRIu64 +            " byte signed integer value", +            sval64, static_cast<uint64_t>(byte_size)); +      else { +        m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize(byte_size); +        switch (m_type) { +        case e_sint: +          m_integer = llvm::APInt(sizeof(sint_t) * 8, sval64, true); +          break; +        case e_slong: +          m_integer = llvm::APInt(sizeof(slong_t) * 8, sval64, true); +          break; +        case e_slonglong: +          m_integer = llvm::APInt(sizeof(slonglong_t) * 8, sval64, true); +          break; +        default: +          error.SetErrorStringWithFormat( +              "unsupported signed integer byte size: %" PRIu64 "", +              static_cast<uint64_t>(byte_size)); +          break; +        } +      } +    } else { +      error.SetErrorStringWithFormat( +          "unsupported signed integer byte size: %" PRIu64 "", +          static_cast<uint64_t>(byte_size)); +      return error; +    } +    break; + +  case eEncodingIEEE754: +    static float f_val; +    static double d_val; +    static long double l_val; +    if (byte_size == sizeof(float)) { +      if (::sscanf(value_str, "%f", &f_val) == 1) { +        m_float = llvm::APFloat(f_val); +        m_type = e_float; +      } else +        error.SetErrorStringWithFormat("'%s' is not a valid float string value", +                                       value_str); +    } else if (byte_size == sizeof(double)) { +      if (::sscanf(value_str, "%lf", &d_val) == 1) { +        m_float = llvm::APFloat(d_val); +        m_type = e_double; +      } else +        error.SetErrorStringWithFormat("'%s' is not a valid float string value", +                                       value_str); +    } else if (byte_size == sizeof(long double)) { +      if (::sscanf(value_str, "%Lf", &l_val) == 1) { +        m_float = llvm::APFloat( +            llvm::APFloat::x87DoubleExtended(), +            llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, +                        (reinterpret_cast<type128 *>(&l_val))->x)); +        m_type = e_long_double; +      } else +        error.SetErrorStringWithFormat("'%s' is not a valid float string value", +                                       value_str); +    } else { +      error.SetErrorStringWithFormat("unsupported float byte size: %" PRIu64 "", +                                     static_cast<uint64_t>(byte_size)); +      return error; +    } +    break; + +  case eEncodingVector: +    error.SetErrorString("vector encoding unsupported."); +    break; +  } +  if (error.Fail()) +    m_type = e_void; + +  return error; +} + +Status Scalar::SetValueFromData(DataExtractor &data, lldb::Encoding encoding, +                                size_t byte_size) { +  Status error; + +  type128 int128; +  type256 int256; +  switch (encoding) { +  case lldb::eEncodingInvalid: +    error.SetErrorString("invalid encoding"); +    break; +  case lldb::eEncodingVector: +    error.SetErrorString("vector encoding unsupported"); +    break; +  case lldb::eEncodingUint: { +    lldb::offset_t offset = 0; + +    switch (byte_size) { +    case 1: +      operator=(data.GetU8(&offset)); +      break; +    case 2: +      operator=(data.GetU16(&offset)); +      break; +    case 4: +      operator=(data.GetU32(&offset)); +      break; +    case 8: +      operator=(data.GetU64(&offset)); +      break; +    case 16: +      if (data.GetByteOrder() == eByteOrderBig) { +        int128.x[1] = data.GetU64(&offset); +        int128.x[0] = data.GetU64(&offset); +      } else { +        int128.x[0] = data.GetU64(&offset); +        int128.x[1] = data.GetU64(&offset); +      } +      operator=(llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, int128.x)); +      break; +    case 32: +      if (data.GetByteOrder() == eByteOrderBig) { +        int256.x[3] = data.GetU64(&offset); +        int256.x[2] = data.GetU64(&offset); +        int256.x[1] = data.GetU64(&offset); +        int256.x[0] = data.GetU64(&offset); +      } else { +        int256.x[0] = data.GetU64(&offset); +        int256.x[1] = data.GetU64(&offset); +        int256.x[2] = data.GetU64(&offset); +        int256.x[3] = data.GetU64(&offset); +      } +      operator=(llvm::APInt(BITWIDTH_INT256, NUM_OF_WORDS_INT256, int256.x)); +      break; +    default: +      error.SetErrorStringWithFormat( +          "unsupported unsigned integer byte size: %" PRIu64 "", +          static_cast<uint64_t>(byte_size)); +      break; +    } +  } break; +  case lldb::eEncodingSint: { +    lldb::offset_t offset = 0; + +    switch (byte_size) { +    case 1: +      operator=(static_cast<int8_t>(data.GetU8(&offset))); +      break; +    case 2: +      operator=(static_cast<int16_t>(data.GetU16(&offset))); +      break; +    case 4: +      operator=(static_cast<int32_t>(data.GetU32(&offset))); +      break; +    case 8: +      operator=(static_cast<int64_t>(data.GetU64(&offset))); +      break; +    case 16: +      if (data.GetByteOrder() == eByteOrderBig) { +        int128.x[1] = data.GetU64(&offset); +        int128.x[0] = data.GetU64(&offset); +      } else { +        int128.x[0] = data.GetU64(&offset); +        int128.x[1] = data.GetU64(&offset); +      } +      operator=(llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, int128.x)); +      break; +    case 32: +      if (data.GetByteOrder() == eByteOrderBig) { +        int256.x[3] = data.GetU64(&offset); +        int256.x[2] = data.GetU64(&offset); +        int256.x[1] = data.GetU64(&offset); +        int256.x[0] = data.GetU64(&offset); +      } else { +        int256.x[0] = data.GetU64(&offset); +        int256.x[1] = data.GetU64(&offset); +        int256.x[2] = data.GetU64(&offset); +        int256.x[3] = data.GetU64(&offset); +      } +      operator=(llvm::APInt(BITWIDTH_INT256, NUM_OF_WORDS_INT256, int256.x)); +      break; +    default: +      error.SetErrorStringWithFormat( +          "unsupported signed integer byte size: %" PRIu64 "", +          static_cast<uint64_t>(byte_size)); +      break; +    } +  } 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: +    case Scalar::e_double: +    case Scalar::e_long_double: +      return false; + +    case Scalar::e_sint: +    case Scalar::e_uint: +    case Scalar::e_slong: +    case Scalar::e_ulong: +    case Scalar::e_slonglong: +    case Scalar::e_ulonglong: +    case Scalar::e_sint128: +    case Scalar::e_uint128: +    case Scalar::e_sint256: +    case Scalar::e_uint256: +    case Scalar::e_sint512: +    case Scalar::e_uint512: +      if (max_bit_pos == sign_bit_pos) +        return true; +      else 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()) { +          const llvm::APInt mask = +              ~(sign_bit) + llvm::APInt(m_integer.getBitWidth(), 1); +          m_integer |= mask; +        } +        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: +  case Scalar::e_double: +  case Scalar::e_long_double: +    break; + +  case Scalar::e_sint: +  case Scalar::e_slong: +  case Scalar::e_slonglong: +  case Scalar::e_sint128: +  case Scalar::e_sint256: +  case Scalar::e_sint512: +    m_integer = m_integer.ashr(bit_offset) +                    .sextOrTrunc(bit_size) +                    .sextOrSelf(8 * GetByteSize()); +    return true; + +  case Scalar::e_uint: +  case Scalar::e_ulong: +  case Scalar::e_ulonglong: +  case Scalar::e_uint128: +  case Scalar::e_uint256: +  case Scalar::e_uint512: +    m_integer = m_integer.lshr(bit_offset) +                    .zextOrTrunc(bit_size) +                    .zextOrSelf(8 * GetByteSize()); +    return true; +  } +  return false; +} + +bool lldb_private::operator==(const Scalar &lhs, const 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; + +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  llvm::APFloat::cmpResult result; +  switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) { +  case Scalar::e_void: +    break; +  case Scalar::e_sint: +  case Scalar::e_uint: +  case Scalar::e_slong: +  case Scalar::e_ulong: +  case Scalar::e_slonglong: +  case Scalar::e_ulonglong: +  case Scalar::e_sint128: +  case Scalar::e_uint128: +  case Scalar::e_sint256: +  case Scalar::e_uint256: +  case Scalar::e_sint512: +  case Scalar::e_uint512: +    return a->m_integer == b->m_integer; +  case Scalar::e_float: +  case Scalar::e_double: +  case Scalar::e_long_double: +    result = a->m_float.compare(b->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<(const Scalar &lhs, const Scalar &rhs) { +  if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) +    return false; + +  Scalar temp_value; +  const Scalar *a; +  const Scalar *b; +  llvm::APFloat::cmpResult result; +  switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) { +  case Scalar::e_void: +    break; +  case Scalar::e_sint: +  case Scalar::e_slong: +  case Scalar::e_slonglong: +  case Scalar::e_sint128: +  case Scalar::e_sint256: +  case Scalar::e_sint512: +  case Scalar::e_uint512: +    return a->m_integer.slt(b->m_integer); +  case Scalar::e_uint: +  case Scalar::e_ulong: +  case Scalar::e_ulonglong: +  case Scalar::e_uint128: +  case Scalar::e_uint256: +    return a->m_integer.ult(b->m_integer); +  case Scalar::e_float: +  case Scalar::e_double: +  case Scalar::e_long_double: +    result = a->m_float.compare(b->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_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    m_integer.clearBit(bit); +    return true; +  case e_float: +  case e_double: +  case e_long_double: +    break; +  } +  return false; +} + +bool Scalar::SetBit(uint32_t bit) { +  switch (m_type) { +  case e_void: +    break; +  case e_sint: +  case e_uint: +  case e_slong: +  case e_ulong: +  case e_slonglong: +  case e_ulonglong: +  case e_sint128: +  case e_uint128: +  case e_sint256: +  case e_uint256: +  case e_sint512: +  case e_uint512: +    m_integer.setBit(bit); +    return true; +  case e_float: +  case e_double: +  case e_long_double: +    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/lldb/source/Utility/SelectHelper.cpp b/lldb/source/Utility/SelectHelper.cpp new file mode 100644 index 0000000000000..9f5ca586e1ef9 --- /dev/null +++ b/lldb/source/Utility/SelectHelper.cpp @@ -0,0 +1,254 @@ +//===-- SelectHelper.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 +// +//===----------------------------------------------------------------------===// + +#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 "llvm/ADT/Optional.h" + +#include <algorithm> +#include <chrono> + +#include <errno.h> +#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(llvm::Optional<lldb::socket_t> &vold, +                        lldb::socket_t vnew) { +  if (!vold.hasValue()) +    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 + +  llvm::Optional<lldb::socket_t> max_read_fd; +  llvm::Optional<lldb::socket_t> max_write_fd; +  llvm::Optional<lldb::socket_t> max_error_fd; +  llvm::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.hasValue()) { +    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.hasValue()) { +    read_fdset.resize((nfds / FD_SETSIZE) + 1); +    read_fdset_ptr = read_fdset.data(); +  } +  if (max_write_fd.hasValue()) { +    write_fdset.resize((nfds / FD_SETSIZE) + 1); +    write_fdset_ptr = write_fdset.data(); +  } +  if (max_error_fd.hasValue()) { +    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.hasValue()) { +    FD_ZERO(&read_fdset); +    read_fdset_ptr = &read_fdset; +  } +  if (max_write_fd.hasValue()) { +    FD_ZERO(&write_fdset); +    write_fdset_ptr = &write_fdset; +  } +  if (max_error_fd.hasValue()) { +    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.hasValue()) { +      tv_ptr = &tv; +      const auto remaining_dur = duration_cast<microseconds>( +          m_end_time.getValue() - 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/lldb/source/Utility/SharingPtr.cpp b/lldb/source/Utility/SharingPtr.cpp new file mode 100644 index 0000000000000..45f2a773758b7 --- /dev/null +++ b/lldb/source/Utility/SharingPtr.cpp @@ -0,0 +1,134 @@ +//===---------------------SharingPtr.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/SharingPtr.h" + +#if defined(ENABLE_SP_LOGGING) + +// If ENABLE_SP_LOGGING is defined, then log all shared pointer assignments and +// allow them to be queried using a pointer by a call to: +#include <assert.h> +#include <execinfo.h> + +#include "llvm/ADT/STLExtras.h" + +#include <map> +#include <mutex> +#include <vector> + +class Backtrace { +public: +  Backtrace(); + +  ~Backtrace(); + +  void GetFrames(); + +  void Dump() const; + +private: +  void *m_sp_this; +  std::vector<void *> m_frames; +}; + +Backtrace::Backtrace() : m_frames() {} + +Backtrace::~Backtrace() {} + +void Backtrace::GetFrames() { +  void *frames[1024]; +  const int count = ::backtrace(frames, llvm::array_lengthof(frames)); +  if (count > 2) +    m_frames.assign(frames + 2, frames + (count - 2)); +} + +void Backtrace::Dump() const { +  if (!m_frames.empty()) +    ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), STDOUT_FILENO); +  write(STDOUT_FILENO, "\n\n", 2); +} + +extern "C" void track_sp(void *sp_this, void *ptr, long use_count) { +  typedef std::pair<void *, Backtrace> PtrBacktracePair; +  typedef std::map<void *, PtrBacktracePair> PtrToBacktraceMap; +  static std::mutex g_mutex; +  std::lock_guard<std::mutex> guard(g_mutex); +  static PtrToBacktraceMap g_map; + +  if (sp_this) { +    printf("sp(%p) -> %p %lu\n", sp_this, ptr, use_count); + +    if (ptr) { +      Backtrace bt; +      bt.GetFrames(); +      g_map[sp_this] = std::make_pair(ptr, bt); +    } else { +      g_map.erase(sp_this); +    } +  } else { +    if (ptr) +      printf("Searching for shared pointers that are tracking %p: ", ptr); +    else +      printf("Dump all live shared pointres: "); + +    uint32_t matches = 0; +    PtrToBacktraceMap::iterator pos, end = g_map.end(); +    for (pos = g_map.begin(); pos != end; ++pos) { +      if (ptr == NULL || pos->second.first == ptr) { +        ++matches; +        printf("\nsp(%p): %p\n", pos->first, pos->second.first); +        pos->second.second.Dump(); +      } +    } +    if (matches == 0) { +      printf("none.\n"); +    } +  } +} +// Put dump_sp_refs in the lldb namespace to it gets through our exports lists +// filter in the LLDB.framework or lldb.so +namespace lldb { + +void dump_sp_refs(void *ptr) { +  // Use a specially crafted call to "track_sp" which will dump info on all +  // live shared pointers that reference "ptr" +  track_sp(NULL, ptr, 0); +} +} + +#endif + +namespace lldb_private { + +namespace imp { + +shared_count::~shared_count() {} + +void shared_count::add_shared() { +#ifdef _MSC_VER +  _InterlockedIncrement(&shared_owners_); +#else +  ++shared_owners_; +#endif +} + +void shared_count::release_shared() { +#ifdef _MSC_VER +  if (_InterlockedDecrement(&shared_owners_) == -1) +#else +  if (--shared_owners_ == -1) +#endif +  { +    on_zero_shared(); +    delete this; +  } +} + +} // imp + +} // namespace lldb diff --git a/lldb/source/Utility/State.cpp b/lldb/source/Utility/State.cpp new file mode 100644 index 0000000000000..51fe92bad77e3 --- /dev/null +++ b/lldb/source/Utility/State.cpp @@ -0,0 +1,110 @@ +//===-- State.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/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/lldb/source/Utility/Status.cpp b/lldb/source/Utility/Status.cpp new file mode 100644 index 0000000000000..3d64fb810abfc --- /dev/null +++ b/lldb/source/Utility/Status.cpp @@ -0,0 +1,296 @@ +//===-- Status.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/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 <stdint.h> + +namespace llvm { +class raw_ostream; +} + +using namespace lldb; +using namespace lldb_private; + +Status::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {} + +Status::Status(ValueType err, ErrorType type) +    : m_code(err), m_type(type), m_string() {} + +Status::Status(std::error_code EC) +    : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric), +      m_string(EC.message()) {} + +Status::Status(const char *format, ...) +    : m_code(0), m_type(eErrorTypeInvalid), 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::make_error<llvm::StringError>(AsCString(), +                                             llvm::inconvertibleErrorCode()); +} + +// Assignment operator +const Status &Status::operator=(const Status &rhs) { +  if (this != &rhs) { +    m_code = rhs.m_code; +    m_type = rhs.m_type; +    m_string = rhs.m_string; +  } +  return *this; +} + +Status::~Status() = default; + +#ifdef _WIN32 +static std::string RetrieveWin32ErrorString(uint32_t error_code) { +  char *buffer = nullptr; +  std::string message; +  // Retrieve win32 system error. +  if (::FormatMessageA( +          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | +              FORMAT_MESSAGE_MAX_WIDTH_MASK, +          NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +          (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; } + +// Set accessor for the error value to "err" and the type to +// "eErrorTypeMachKernel" +void Status::SetMachError(uint32_t err) { +  m_code = err; +  m_type = eErrorTypeMachKernel; +  m_string.clear(); +} + +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 = 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 = 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; } + +bool Status::WasInterrupted() const { +  return (m_type == eErrorTypePOSIX && m_code == EINTR); +} + +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/lldb/source/Utility/Stream.cpp b/lldb/source/Utility/Stream.cpp new file mode 100644 index 0000000000000..c48a12acd9064 --- /dev/null +++ b/lldb/source/Utility/Stream.cpp @@ -0,0 +1,441 @@ +//===-- Stream.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/Stream.h" + +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/VASPrintf.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/LEB128.h" + +#include <string> + +#include <inttypes.h> +#include <stddef.h> + +using namespace lldb; +using namespace lldb_private; + +Stream::Stream(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) +    : m_flags(flags), m_addr_size(addr_size), m_byte_order(byte_order), +      m_indent_level(0), m_forwarder(*this) {} + +Stream::Stream() +    : m_flags(0), m_addr_size(4), m_byte_order(endian::InlHostByteOrder()), +      m_indent_level(0), m_forwarder(*this) {} + +// Destructor +Stream::~Stream() {} + +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; +} + +// 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 Stream::Address(uint64_t addr, uint32_t addr_size, const char *prefix, +                     const char *suffix) { +  if (prefix == nullptr) +    prefix = ""; +  if (suffix == nullptr) +    suffix = ""; +  //    int addr_width = m_addr_size << 1; +  //    Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_width, addr, suffix); +  Printf("%s0x%0*" PRIx64 "%s", prefix, addr_size * 2, addr, suffix); +} + +// Put an address range out to the stream with optional prefix and suffix +// strings. +void Stream::AddressRange(uint64_t lo_addr, uint64_t hi_addr, +                          uint32_t addr_size, const char *prefix, +                          const char *suffix) { +  if (prefix && prefix[0]) +    PutCString(prefix); +  Address(lo_addr, addr_size, "["); +  Address(hi_addr, addr_size, "-", ")"); +  if (suffix && suffix[0]) +    PutCString(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'); } + +// Indent the current line using the current indentation level and print an +// optional string following the indentation spaces. +size_t Stream::Indent(const char *s) { +  return Printf("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : ""); +} + +size_t Stream::Indent(llvm::StringRef str) { +  return Printf("%*.*s%s", m_indent_level, m_indent_level, "", +                str.str().c_str()); +} + +// 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; +} + +// Stream a uint8_t "uval" out to this stream. +Stream &Stream::operator<<(uint8_t uval) { +  PutHex8(uval); +  return *this; +} + +// Stream a uint16_t "uval" out to this stream. +Stream &Stream::operator<<(uint16_t uval) { +  PutHex16(uval, m_byte_order); +  return *this; +} + +// Stream a uint32_t "uval" out to this stream. +Stream &Stream::operator<<(uint32_t uval) { +  PutHex32(uval, m_byte_order); +  return *this; +} + +// Stream a uint64_t "uval" out to this stream. +Stream &Stream::operator<<(uint64_t uval) { +  PutHex64(uval, m_byte_order); +  return *this; +} + +// Stream a int8_t "sval" out to this stream. +Stream &Stream::operator<<(int8_t sval) { +  Printf("%i", static_cast<int>(sval)); +  return *this; +} + +// Stream a int16_t "sval" out to this stream. +Stream &Stream::operator<<(int16_t sval) { +  Printf("%i", static_cast<int>(sval)); +  return *this; +} + +// Stream a int32_t "sval" out to this stream. +Stream &Stream::operator<<(int32_t sval) { +  Printf("%i", static_cast<int>(sval)); +  return *this; +} + +// Stream a int64_t "sval" out to this stream. +Stream &Stream::operator<<(int64_t sval) { +  Printf("%" PRIi64, sval); +  return *this; +} + +// Get the current indentation level +int Stream::GetIndentLevel() const { return m_indent_level; } + +// Set the current indentation level +void Stream::SetIndentLevel(int indent_level) { m_indent_level = indent_level; } + +// Increment the current indentation level +void Stream::IndentMore(int amount) { m_indent_level += amount; } + +// Decrement the current indentation level +void Stream::IndentLess(int 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 - 1; i < src_len; --i) +      _PutHex8(src[i], 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 - 1; i < src_len; --i) +      _PutHex8(src[i], 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/lldb/source/Utility/StreamCallback.cpp b/lldb/source/Utility/StreamCallback.cpp new file mode 100644 index 0000000000000..b3d3adea78bc5 --- /dev/null +++ b/lldb/source/Utility/StreamCallback.cpp @@ -0,0 +1,22 @@ +//===-- StreamCallback.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/StreamCallback.h" + +#include <string> + +using namespace lldb_private; + +StreamCallback::StreamCallback(lldb::LogOutputCallback callback, void *baton) +    : llvm::raw_ostream(true), m_callback(callback), m_baton(baton) {} + +void StreamCallback::write_impl(const char *Ptr, size_t Size) { +  m_callback(std::string(Ptr, Size).c_str(), m_baton); +} + +uint64_t StreamCallback::current_pos() const { return 0; } diff --git a/lldb/source/Utility/StreamString.cpp b/lldb/source/Utility/StreamString.cpp new file mode 100644 index 0000000000000..bf9814d6c3055 --- /dev/null +++ b/lldb/source/Utility/StreamString.cpp @@ -0,0 +1,66 @@ +//===-- StreamString.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/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +StreamString::StreamString() : Stream(0, 4, eByteOrderBig) {} + +StreamString::StreamString(uint32_t flags, uint32_t addr_size, +                           ByteOrder byte_order) +    : Stream(flags, addr_size, byte_order), m_packet() {} + +StreamString::~StreamString() {} + +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(reinterpret_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/lldb/source/Utility/StringExtractor.cpp b/lldb/source/Utility/StringExtractor.cpp new file mode 100644 index 0000000000000..87fe4f13e450c --- /dev/null +++ b/lldb/source/Utility/StringExtractor.cpp @@ -0,0 +1,370 @@ +//===-- StringExtractor.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/StringExtractor.h" + +#include <tuple> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +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(), m_index(0) {} + +StringExtractor::StringExtractor(llvm::StringRef packet_str) +    : m_packet(), m_index(0) { +  m_packet.assign(packet_str.begin(), packet_str.end()); +} + +StringExtractor::StringExtractor(const char *packet_cstr) +    : m_packet(), m_index(0) { +  if (packet_cstr) +    m_packet.assign(packet_cstr); +} + +// Destructor +StringExtractor::~StringExtractor() {} + +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.startswith(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 && isspace(m_packet[m_index])) +    ++m_index; +} diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp new file mode 100644 index 0000000000000..a011e9246d15a --- /dev/null +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -0,0 +1,604 @@ +//===-- StringExtractorGDBRemote.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/StringExtractorGDBRemote.h" + +#include <ctype.h> +#include <string.h> + +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("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 '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; +      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; +      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("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_STARTS_WITH("jTraceBufferRead:")) +      return eServerPacketType_jTraceBufferRead; +    if (PACKET_STARTS_WITH("jTraceConfigRead:")) +      return eServerPacketType_jTraceConfigRead; +    if (PACKET_STARTS_WITH("jTraceMetaRead:")) +      return eServerPacketType_jTraceMetaRead; +    if (PACKET_STARTS_WITH("jTraceStart:")) +      return eServerPacketType_jTraceStart; +    if (PACKET_STARTS_WITH("jTraceStop:")) +      return eServerPacketType_jTraceStop; +    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: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; +    } +    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': +    if (packet_size == 1) +      return eServerPacketType_D; +    break; + +  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 +} diff --git a/lldb/source/Utility/StringLexer.cpp b/lldb/source/Utility/StringLexer.cpp new file mode 100644 index 0000000000000..c357cb0fb5530 --- /dev/null +++ b/lldb/source/Utility/StringLexer.cpp @@ -0,0 +1,84 @@ +//===--------------------- StringLexer.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/StringLexer.h" + +#include <algorithm> +#include <assert.h> + +using namespace lldb_private; + +StringLexer::StringLexer(std::string s) : m_data(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/lldb/source/Utility/StringList.cpp b/lldb/source/Utility/StringList.cpp new file mode 100644 index 0000000000000..5e06b6b69fc09 --- /dev/null +++ b/lldb/source/Utility/StringList.cpp @@ -0,0 +1,241 @@ +//===-- StringList.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/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 <stdint.h> +#include <string.h> + +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() {} + +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(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::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::makeArrayRef(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, str); +  else +    m_strings.push_back(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 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<<(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/lldb/source/Utility/StructuredData.cpp b/lldb/source/Utility/StructuredData.cpp new file mode 100644 index 0000000000000..783a080821744 --- /dev/null +++ b/lldb/source/Utility/StructuredData.cpp @@ -0,0 +1,176 @@ +//===---------------------StructuredData.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/StructuredData.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cerrno> +#include <cstdlib> +#include <inttypes.h> +#include <limits> + +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(std::string 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; +  } +  return ParseJSON(buffer_or_error.get()->getBuffer().str()); +} + +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); + +  std::string s; +  if (json::fromJSON(value, s)) +    return std::make_shared<StructuredData::String>(s); + +  bool b; +  if (json::fromJSON(value, b)) +    return std::make_shared<StructuredData::Boolean>(b); + +  int64_t i; +  if (json::fromJSON(value, i)) +    return std::make_shared<StructuredData::Integer>(i); + +  double d; +  if (json::fromJSON(value, d)) +    return std::make_shared<StructuredData::Float>(d); + +  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 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 array_up; +} + +StructuredData::ObjectSP +StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) { +  if (this->GetType() == lldb::eStructuredDataTypeDictionary) { +    std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.'); +    std::string key = match.first.str(); +    ObjectSP value = this->GetAsDictionary()->GetValueForKey(key); +    if (value.get()) { +      // Do we have additional words to descend?  If not, return the value +      // we're at right now. +      if (match.second.empty()) { +        return value; +      } else { +        return value->GetObjectForDotSeparatedPath(match.second); +      } +    } +    return ObjectSP(); +  } + +  if (this->GetType() == lldb::eStructuredDataTypeArray) { +    std::pair<llvm::StringRef, llvm::StringRef> match = path.split('['); +    if (match.second.empty()) { +      return this->shared_from_this(); +    } +    errno = 0; +    uint64_t val = strtoul(match.second.str().c_str(), nullptr, 10); +    if (errno == 0) { +      return this->GetAsArray()->GetItemAtIndex(val); +    } +    return ObjectSP(); +  } + +  return this->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::Integer::Serialize(json::OStream &s) const { +  s.value(static_cast<int64_t>(m_value)); +} + +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(); +  for (const auto &pair : m_dict) { +    s.attributeBegin(pair.first.AsCString()); +    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)); +} diff --git a/lldb/source/Utility/TildeExpressionResolver.cpp b/lldb/source/Utility/TildeExpressionResolver.cpp new file mode 100644 index 0000000000000..b58f45728ce7e --- /dev/null +++ b/lldb/source/Utility/TildeExpressionResolver.cpp @@ -0,0 +1,93 @@ +//===--------------------- TildeExpressionResolver.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/TildeExpressionResolver.h" + +#include <assert.h> +#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() {} + +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.startswith(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) { +  Output.clear(); +  if (!Expr.startswith("~")) { +    Output.append(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)) +    return false; + +  Output.append(Expr.begin() + Left.size(), Expr.end()); +  return true; +} diff --git a/lldb/source/Utility/Timer.cpp b/lldb/source/Utility/Timer.cpp new file mode 100644 index 0000000000000..6b46d8ba73642 --- /dev/null +++ b/lldb/source/Utility/Timer.cpp @@ -0,0 +1,152 @@ +//===-- Timer.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/Timer.h" +#include "lldb/Utility/Stream.h" + +#include <algorithm> +#include <map> +#include <mutex> +#include <utility> +#include <vector> + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +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 + +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()) { +  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; + +  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.begin(), sorted.end(), 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/lldb/source/Utility/UUID.cpp b/lldb/source/Utility/UUID.cpp new file mode 100644 index 0000000000000..2a73f9a482ffd --- /dev/null +++ b/lldb/source/Utility/UUID.cpp @@ -0,0 +1,123 @@ +//===-- UUID.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/UUID.h" + +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +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; +  } +} + +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, +                                uint32_t num_uuid_bytes) { +  uuid_bytes.clear(); +  while (!p.empty()) { +    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); + +      // Increment the byte that we are decoding within the UUID value and +      // break out if we are done +      if (uuid_bytes.size() == num_uuid_bytes) +        break; +    } else if (p.front() == '-') { +      // Skip dashes +      p = p.drop_front(); +    } else { +      // UUID values can only consist of hex characters and '-' chars +      break; +    } +  } +  return p; +} + +size_t UUID::SetFromStringRef(llvm::StringRef str, uint32_t num_uuid_bytes) { +  llvm::StringRef p = str; + +  // Skip leading whitespace characters +  p = p.ltrim(); + +  llvm::SmallVector<uint8_t, 20> bytes; +  llvm::StringRef rest = +      UUID::DecodeUUIDBytesFromString(p, bytes, num_uuid_bytes); + +  // If we successfully decoded a UUID, return the amount of characters that +  // were consumed +  if (bytes.size() == num_uuid_bytes) { +    *this = fromData(bytes); +    return str.size() - rest.size(); +  } + +  // Else return zero to indicate we were not able to parse a UUID value +  return 0; +} + +size_t UUID::SetFromOptionalStringRef(llvm::StringRef str,  +                                      uint32_t num_uuid_bytes) { +  size_t num_chars_consumed = SetFromStringRef(str, num_uuid_bytes); +  if (num_chars_consumed) { +    if (llvm::all_of(m_bytes, [](uint8_t b) { return b == 0; })) +        Clear(); +  } +   +  return num_chars_consumed; +} + diff --git a/lldb/source/Utility/UriParser.cpp b/lldb/source/Utility/UriParser.cpp new file mode 100644 index 0000000000000..b446958f2f47a --- /dev/null +++ b/lldb/source/Utility/UriParser.cpp @@ -0,0 +1,70 @@ +//===-- UriParser.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/UriParser.h" + +#include <string> + +#include <stdint.h> +#include <tuple> + +using namespace lldb_private; + +// UriParser::Parse +bool UriParser::Parse(llvm::StringRef uri, llvm::StringRef &scheme, +                      llvm::StringRef &hostname, int &port, +                      llvm::StringRef &path) { +  llvm::StringRef tmp_scheme, tmp_hostname, tmp_path; + +  const llvm::StringRef kSchemeSep("://"); +  auto pos = uri.find(kSchemeSep); +  if (pos == std::string::npos) +    return false; + +  // Extract path. +  tmp_scheme = uri.substr(0, pos); +  auto host_pos = pos + kSchemeSep.size(); +  auto path_pos = uri.find('/', host_pos); +  if (path_pos != std::string::npos) +    tmp_path = uri.substr(path_pos); +  else +    tmp_path = "/"; + +  auto host_port = uri.substr( +      host_pos, +      ((path_pos != std::string::npos) ? path_pos : uri.size()) - host_pos); + +  // Extract hostname +  if (!host_port.empty() && host_port[0] == '[') { +    // hostname is enclosed with square brackets. +    pos = host_port.find(']'); +    if (pos == std::string::npos) +      return false; + +    tmp_hostname = host_port.substr(1, pos - 1); +    host_port = host_port.drop_front(pos + 1); +    if (!host_port.empty() && !host_port.consume_front(":")) +      return false; +  } else { +    std::tie(tmp_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 false; +    port = port_value; +  } else +    port = -1; + +  scheme = tmp_scheme; +  hostname = tmp_hostname; +  path = tmp_path; +  return true; +} diff --git a/lldb/source/Utility/UserID.cpp b/lldb/source/Utility/UserID.cpp new file mode 100644 index 0000000000000..b76a1cd84f824 --- /dev/null +++ b/lldb/source/Utility/UserID.cpp @@ -0,0 +1,20 @@ +//===-- UserID.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/UserID.h" +#include "lldb/Utility/Stream.h" + +#include <inttypes.h> + +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/lldb/source/Utility/UserIDResolver.cpp b/lldb/source/Utility/UserIDResolver.cpp new file mode 100644 index 0000000000000..8aac6f948cd2f --- /dev/null +++ b/lldb/source/Utility/UserIDResolver.cpp @@ -0,0 +1,44 @@ +//===-- UserIDResolver.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/UserIDResolver.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace lldb_private; + +UserIDResolver::~UserIDResolver() = default; + +llvm::Optional<llvm::StringRef> UserIDResolver::Get( +    id_t id, Map &cache, +    llvm::Optional<std::string> (UserIDResolver::*do_get)(id_t)) { + +  std::lock_guard<std::mutex> guard(m_mutex); +  auto iter_bool = cache.try_emplace(id, llvm::None); +  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 llvm::None; +} + +namespace { +class NoopResolver : public UserIDResolver { +protected: +  llvm::Optional<std::string> DoGetUserName(id_t uid) override { +    return llvm::None; +  } + +  llvm::Optional<std::string> DoGetGroupName(id_t gid) override { +    return llvm::None; +  } +}; +} // namespace + +static llvm::ManagedStatic<NoopResolver> g_noop_resolver; + +UserIDResolver &UserIDResolver::GetNoopResolver() { return *g_noop_resolver; } diff --git a/lldb/source/Utility/UuidCompatibility.h b/lldb/source/Utility/UuidCompatibility.h new file mode 100644 index 0000000000000..e992c0c79a1f8 --- /dev/null +++ b/lldb/source/Utility/UuidCompatibility.h @@ -0,0 +1,17 @@ +//===-- UuidCompatibility.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 +// +//===----------------------------------------------------------------------===// + +// Include this header if your system does not have a definition of uuid_t + +#ifndef utility_UUID_COMPATIBILITY_H +#define utility_UUID_COMPATIBILITY_H + +// uuid_t is guaranteed to always be a 16-byte array +typedef unsigned char uuid_t[16]; + +#endif // utility_UUID_COMPATIBILITY_H diff --git a/lldb/source/Utility/VASprintf.cpp b/lldb/source/Utility/VASprintf.cpp new file mode 100644 index 0000000000000..2ee0f6676fa7e --- /dev/null +++ b/lldb/source/Utility/VASprintf.cpp @@ -0,0 +1,55 @@ +//===-- VASprintf.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/VASPrintf.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +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/lldb/source/Utility/VMRange.cpp b/lldb/source/Utility/VMRange.cpp new file mode 100644 index 0000000000000..f3f4ae7efc3c1 --- /dev/null +++ b/lldb/source/Utility/VMRange.cpp @@ -0,0 +1,70 @@ +//===-- VMRange.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/VMRange.h" + +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-types.h" + +#include <algorithm> +#include <iterator> +#include <vector> + +#include <stddef.h> +#include <stdint.h> + +using namespace lldb; +using namespace lldb_private; + +bool VMRange::ContainsValue(const VMRange::collection &coll, +                            lldb::addr_t value) { +  return llvm::find_if(coll, [&](const VMRange &r) { +           return r.Contains(value); +         }) != coll.end(); +} + +bool VMRange::ContainsRange(const VMRange::collection &coll, +                            const VMRange &range) { +  return llvm::find_if(coll, [&](const VMRange &r) { +           return r.Contains(range); +         }) != coll.end(); +} + +void VMRange::Dump(Stream *s, lldb::addr_t offset, uint32_t addr_width) const { +  s->AddressRange(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); +}  | 
