diff options
author | dim <dim@FreeBSD.org> | 2017-09-26 19:56:36 +0000 |
---|---|---|
committer | Luiz Souza <luiz@netgate.com> | 2018-02-21 15:12:19 -0300 |
commit | 1dcd2e8d24b295bc73e513acec2ed1514bb66be4 (patch) | |
tree | 4bd13a34c251e980e1a6b13584ca1f63b0dfe670 /contrib/llvm/tools/lldb/source/Utility | |
parent | f45541ca2a56a1ba1202f94c080b04e96c1fa239 (diff) | |
download | FreeBSD-src-1dcd2e8d24b295bc73e513acec2ed1514bb66be4.zip FreeBSD-src-1dcd2e8d24b295bc73e513acec2ed1514bb66be4.tar.gz |
Merge clang, llvm, lld, lldb, compiler-rt and libc++ 5.0.0 release.
MFC r309126 (by emaste):
Correct lld llvm-tblgen dependency file name
MFC r309169:
Get rid of separate Subversion mergeinfo properties for llvm-dwarfdump
and llvm-lto. The mergeinfo confuses Subversion enormously, and these
directories will just use the mergeinfo for llvm itself.
MFC r312765:
Pull in r276136 from upstream llvm trunk (by Wei Mi):
Use ValueOffsetPair to enhance value reuse during SCEV expansion.
In D12090, the ExprValueMap was added to reuse existing value during
SCEV expansion. However, const folding and sext/zext distribution can
make the reuse still difficult.
A simplified case is: suppose we know S1 expands to V1 in
ExprValueMap, and
S1 = S2 + C_a
S3 = S2 + C_b
where C_a and C_b are different SCEVConstants. Then we'd like to
expand S3 as V1 - C_a + C_b instead of expanding S2 literally. It is
helpful when S2 is a complex SCEV expr and S2 has no entry in
ExprValueMap, which is usually caused by the fact that S3 is
generated from S1 after const folding.
In order to do that, we represent ExprValueMap as a mapping from SCEV
to ValueOffsetPair. We will save both S1->{V1, 0} and S2->{V1, C_a}
into the ExprValueMap when we create SCEV for V1. When S3 is
expanded, it will first expand S2 to V1 - C_a because of S2->{V1,
C_a} in the map, then expand S3 to V1 - C_a + C_b.
Differential Revision: https://reviews.llvm.org/D21313
This should fix assertion failures when building OpenCV >= 3.1.
PR: 215649
MFC r312831:
Revert r312765 for now, since it causes assertions when building
lang/spidermonkey24.
Reported by: antoine
PR: 215649
MFC r316511 (by jhb):
Add an implementation of __ffssi2() derived from __ffsdi2().
Newer versions of GCC include an __ffssi2() symbol in libgcc and the
compiler can emit calls to it in generated code. This is true for at
least GCC 6.2 when compiling world for mips and mips64.
Reviewed by: jmallett, dim
Sponsored by: DARPA / AFRL
Differential Revision: https://reviews.freebsd.org/D10086
MFC r318601 (by adrian):
[libcompiler-rt] add bswapdi2/bswapsi2
This is required for mips gcc 6.3 userland to build/run.
Reviewed by: emaste, dim
Approved by: emaste
Differential Revision: https://reviews.freebsd.org/D10838
MFC r318884 (by emaste):
lldb: map TRAP_CAP to a trace trap
In the absense of a more specific handler for TRAP_CAP (generated by
ENOTCAPABLE or ECAPMODE while in capability mode) treat it as a trace
trap.
Example usage (testing the bug in PR219173):
% proccontrol -m trapcap lldb usr.bin/hexdump/obj/hexdump -- -Cv -s 1 /bin/ls
...
(lldb) run
Process 12980 launching
Process 12980 launched: '.../usr.bin/hexdump/obj/hexdump' (x86_64)
Process 12980 stopped
* thread #1, stop reason = trace
frame #0: 0x0000004b80c65f1a libc.so.7`__sys_lseek + 10
...
In the future we should have LLDB control the trapcap procctl itself
(as it does with ASLR), as well as report a specific stop reason.
This change eliminates an assertion failure from LLDB for now.
MFC r319796:
Remove a few unneeded files from libllvm, libclang and liblldb.
MFC r319885 (by emaste):
lld: ELF: Fix ICF crash on absolute symbol relocations.
If two sections contained relocations to absolute symbols with the same
value we would crash when trying to access their sections. Add a check that
both symbols point to sections before accessing their sections, and treat
absolute symbols as equal if their values are equal.
Obtained from: LLD commit r292578
MFC r319918:
Revert r319796 for now, it can cause undefined references when linking
in some circumstances.
Reported by: Shawn Webb <shawn.webb@hardenedbsd.org>
MFC r319957 (by emaste):
lld: Add armelf emulation mode
Obtained from: LLD r305375
MFC r321369:
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
5.0.0 (trunk r308421). Upstream has branched for the 5.0.0 release,
which should be in about a month. Please report bugs and regressions,
so we can get them into the release.
Please note that from 3.5.0 onwards, clang, llvm and lldb require C++11
support to build; see UPDATING for more information.
MFC r321420:
Add a few more object files to liblldb, which should solve errors when
linking the lldb executable in some cases. In particular, when the
-ffunction-sections -fdata-sections options are turned off, or
ineffective.
Reported by: Shawn Webb, Mark Millard
MFC r321433:
Cleanup stale Options.inc files from the previous libllvm build for
clang 4.0.0. Otherwise, these can get included before the two newly
generated ones (which are different) for clang 5.0.0.
Reported by: Mark Millard
MFC r321439 (by bdrewery):
Move llvm Options.inc hack from r321433 for NO_CLEAN to lib/clang/libllvm.
The files are only ever generated to .OBJDIR, not to WORLDTMP (as a
sysroot) and are only ever included from a compilation. So using
a beforebuild target here removes the file before the compilation
tries to include it.
MFC r321664:
Pull in r308891 from upstream llvm trunk (by Benjamin Kramer):
[CodeGenPrepare] Cut off FindAllMemoryUses if there are too many uses.
This avoids excessive compile time. The case I'm looking at is
Function.cpp from an old version of LLVM that still had the giant
memcmp string matcher in it. Before r308322 this compiled in about 2
minutes, after it, clang takes infinite* time to compile it. With
this patch we're at 5 min, which is still bad but this is a
pathological case.
The cut off at 20 uses was chosen by looking at other cut-offs in LLVM
for user scanning. It's probably too high, but does the job and is
very unlikely to regress anything.
Fixes PR33900.
* I'm impatient and aborted after 15 minutes, on the bug report it was
killed after 2h.
Pull in r308986 from upstream llvm trunk (by Simon Pilgrim):
[X86][CGP] Reduce memcmp() expansion to 2 load pairs (PR33914)
D35067/rL308322 attempted to support up to 4 load pairs for memcmp
inlining which resulted in regressions for some optimized libc memcmp
implementations (PR33914).
Until we can match these more optimal cases, this patch reduces the
memcmp expansion to a maximum of 2 load pairs (which matches what we
do for -Os).
This patch should be considered for the 5.0.0 release branch as well
Differential Revision: https://reviews.llvm.org/D35830
These fix a hang (or extremely long compile time) when building older
LLVM ports.
Reported by: antoine
PR: 219139
MFC r321719:
Pull in r309503 from upstream clang trunk (by Richard Smith):
PR33902: Invalidate line number cache when adding more text to
existing buffer.
This led to crashes as the line number cache would report a bogus
line number for a line of code, and we'd try to find a nonexistent
column within the line when printing diagnostics.
This fixes an assertion when building the graphics/champlain port.
Reported by: antoine, kwm
PR: 219139
MFC r321723:
Upgrade our copies of clang, llvm, lld and lldb to r309439 from the
upstream release_50 branch. This is just after upstream's 5.0.0-rc1.
MFC r322320:
Upgrade our copies of clang, llvm and libc++ to r310316 from the
upstream release_50 branch.
MFC r322326 (by emaste):
lldb: Make i386-*-freebsd expression work on JIT path
* Enable i386 ABI creation for freebsd
* Added an extra argument in ABISysV_i386::PrepareTrivialCall for mmap
syscall
* Unlike linux, the last argument of mmap is actually 64-bit(off_t).
This requires us to push an additional word for the higher order bits.
* Prior to this change, ktrace dump will show mmap failures due to
invalid argument coming from the 6th mmap argument.
Submitted by: Karnajit Wangkhem
Differential Revision: https://reviews.llvm.org/D34776
MFC r322360 (by emaste):
lldb: Report inferior signals as signals, not exceptions, on FreeBSD
This is the FreeBSD equivalent of LLVM r238549.
This serves 2 purposes:
* LLDB should handle inferior process signals SIGSEGV/SIGILL/SIGBUS/
SIGFPE the way it is suppose to be handled. Prior to this fix these
signals will neither create a coredump, nor exit from the debugger
or work for signal handling scenario.
* eInvalidCrashReason need not report "unknown crash reason" if we have
a valid si_signo
llvm.org/pr23699
Patch by Karnajit Wangkhem
Differential Revision: https://reviews.llvm.org/D35223
Submitted by: Karnajit Wangkhem
Obtained from: LLVM r310591
MFC r322474 (by emaste):
lld: Add `-z muldefs` option.
Obtained from: LLVM r310757
MFC r322740:
Upgrade our copies of clang, llvm, lld and libc++ to r311219 from the
upstream release_50 branch.
MFC r322855:
Upgrade our copies of clang, llvm, lldb and compiler-rt to r311606 from
the upstream release_50 branch.
As of this version, lib/msun's trig test should also work correctly
again (see bug 220989 for more information).
PR: 220989
MFC r323112:
Upgrade our copies of clang, llvm, lldb and compiler-rt to r312293 from
the upstream release_50 branch. This corresponds to 5.0.0 rc4.
As of this version, the cad/stepcode port should now compile in a more
reasonable time on i386 (see bug 221836 for more information).
PR: 221836
MFC r323245:
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
5.0.0 release (upstream r312559).
Release notes for llvm, clang and lld will be available here soon:
<http://releases.llvm.org/5.0.0/docs/ReleaseNotes.html>
<http://releases.llvm.org/5.0.0/tools/clang/docs/ReleaseNotes.html>
<http://releases.llvm.org/5.0.0/tools/lld/docs/ReleaseNotes.html>
Relnotes: yes
(cherry picked from commit 12cd91cf4c6b96a24427c0de5374916f2808d263)
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Utility')
48 files changed, 8397 insertions, 2237 deletions
diff --git a/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.cpp b/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.cpp deleted file mode 100644 index 8c2a716..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.cpp +++ /dev/null @@ -1,212 +0,0 @@ -//===-- ARM64_DWARF_Registers.cpp -------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include <string.h> - -#include "ARM64_DWARF_Registers.h" - -using namespace lldb; -using namespace lldb_private; -using namespace arm64_dwarf; - -const char *arm64_dwarf::GetRegisterName(unsigned reg_num, - bool altnernate_name) { - if (altnernate_name) { - switch (reg_num) { - case fp: - return "x29"; - case lr: - return "x30"; - case sp: - return "x31"; - default: - break; - } - return nullptr; - } - - switch (reg_num) { - case x0: - return "x0"; - case x1: - return "x1"; - case x2: - return "x2"; - case x3: - return "x3"; - case x4: - return "x4"; - case x5: - return "x5"; - case x6: - return "x6"; - case x7: - return "x7"; - case x8: - return "x8"; - case x9: - return "x9"; - case x10: - return "x10"; - case x11: - return "x11"; - case x12: - return "x12"; - case x13: - return "x13"; - case x14: - return "x14"; - case x15: - return "x15"; - case x16: - return "x16"; - case x17: - return "x17"; - case x18: - return "x18"; - case x19: - return "x19"; - case x20: - return "x20"; - case x21: - return "x21"; - case x22: - return "x22"; - case x23: - return "x23"; - case x24: - return "x24"; - case x25: - return "x25"; - case x26: - return "x26"; - case x27: - return "x27"; - case x28: - return "x28"; - case fp: - return "fp"; - case lr: - return "lr"; - case sp: - return "sp"; - case pc: - return "pc"; - case cpsr: - return "cpsr"; - case v0: - return "v0"; - case v1: - return "v1"; - case v2: - return "v2"; - case v3: - return "v3"; - case v4: - return "v4"; - case v5: - return "v5"; - case v6: - return "v6"; - case v7: - return "v7"; - case v8: - return "v8"; - case v9: - return "v9"; - case v10: - return "v10"; - case v11: - return "v11"; - case v12: - return "v12"; - case v13: - return "v13"; - case v14: - return "v14"; - case v15: - return "v15"; - case v16: - return "v16"; - case v17: - return "v17"; - case v18: - return "v18"; - case v19: - return "v19"; - case v20: - return "v20"; - case v21: - return "v21"; - case v22: - return "v22"; - case v23: - return "v23"; - case v24: - return "v24"; - case v25: - return "v25"; - case v26: - return "v26"; - case v27: - return "v27"; - case v28: - return "v28"; - case v29: - return "v29"; - case v30: - return "v30"; - case v31: - return "v31"; - } - return nullptr; -} - -bool arm64_dwarf::GetRegisterInfo(unsigned reg_num, RegisterInfo ®_info) { - ::memset(®_info, 0, sizeof(RegisterInfo)); - ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); - - if (reg_num >= x0 && reg_num <= pc) { - reg_info.byte_size = 8; - reg_info.format = eFormatHex; - reg_info.encoding = eEncodingUint; - } else if (reg_num >= v0 && reg_num <= v31) { - reg_info.byte_size = 16; - reg_info.format = eFormatVectorOfFloat32; - reg_info.encoding = eEncodingVector; - } else if (reg_num == cpsr) { - reg_info.byte_size = 4; - reg_info.format = eFormatHex; - reg_info.encoding = eEncodingUint; - } else { - return false; - } - - reg_info.name = arm64_dwarf::GetRegisterName(reg_num, false); - reg_info.alt_name = arm64_dwarf::GetRegisterName(reg_num, true); - reg_info.kinds[eRegisterKindDWARF] = reg_num; - - switch (reg_num) { - case fp: - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; - break; - case lr: - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; - break; - case sp: - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; - break; - case pc: - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; - break; - default: - break; - } - return true; -} diff --git a/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.h b/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.h index be0ea2a..ce548a2 100644 --- a/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.h +++ b/contrib/llvm/tools/lldb/source/Utility/ARM64_DWARF_Registers.h @@ -91,10 +91,6 @@ enum { // 96-127 reserved }; -const char *GetRegisterName(unsigned reg_num, bool altnernate_name); - -bool GetRegisterInfo(unsigned reg_num, lldb_private::RegisterInfo ®_info); - } // namespace arm64_dwarf #endif // utility_ARM64_DWARF_Registers_h_ diff --git a/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.cpp b/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.cpp deleted file mode 100644 index c136376..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.cpp +++ /dev/null @@ -1,925 +0,0 @@ -//===-- ARM_DWARF_Registers.cpp ---------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ARM_DWARF_Registers.h" -#include <string.h> - -#include <string.h> - -using namespace lldb; -using namespace lldb_private; - -const char *GetARMDWARFRegisterName(unsigned reg_num) { - switch (reg_num) { - case dwarf_r0: - return "r0"; - case dwarf_r1: - return "r1"; - case dwarf_r2: - return "r2"; - case dwarf_r3: - return "r3"; - case dwarf_r4: - return "r4"; - case dwarf_r5: - return "r5"; - case dwarf_r6: - return "r6"; - case dwarf_r7: - return "r7"; - case dwarf_r8: - return "r8"; - case dwarf_r9: - return "r9"; - case dwarf_r10: - return "r10"; - case dwarf_r11: - return "r11"; - case dwarf_r12: - return "r12"; - case dwarf_sp: - return "sp"; - case dwarf_lr: - return "lr"; - case dwarf_pc: - return "pc"; - case dwarf_cpsr: - return "cpsr"; - - case dwarf_s0: - return "s0"; - case dwarf_s1: - return "s1"; - case dwarf_s2: - return "s2"; - case dwarf_s3: - return "s3"; - case dwarf_s4: - return "s4"; - case dwarf_s5: - return "s5"; - case dwarf_s6: - return "s6"; - case dwarf_s7: - return "s7"; - case dwarf_s8: - return "s8"; - case dwarf_s9: - return "s9"; - case dwarf_s10: - return "s10"; - case dwarf_s11: - return "s11"; - case dwarf_s12: - return "s12"; - case dwarf_s13: - return "s13"; - case dwarf_s14: - return "s14"; - case dwarf_s15: - return "s15"; - case dwarf_s16: - return "s16"; - case dwarf_s17: - return "s17"; - case dwarf_s18: - return "s18"; - case dwarf_s19: - return "s19"; - case dwarf_s20: - return "s20"; - case dwarf_s21: - return "s21"; - case dwarf_s22: - return "s22"; - case dwarf_s23: - return "s23"; - case dwarf_s24: - return "s24"; - case dwarf_s25: - return "s25"; - case dwarf_s26: - return "s26"; - case dwarf_s27: - return "s27"; - case dwarf_s28: - return "s28"; - case dwarf_s29: - return "s29"; - case dwarf_s30: - return "s30"; - case dwarf_s31: - return "s31"; - - // FPA Registers 0-7 - case dwarf_f0: - return "f0"; - case dwarf_f1: - return "f1"; - case dwarf_f2: - return "f2"; - case dwarf_f3: - return "f3"; - case dwarf_f4: - return "f4"; - case dwarf_f5: - return "f5"; - case dwarf_f6: - return "f6"; - case dwarf_f7: - return "f7"; - - // Intel wireless MMX general purpose registers 0 - 7 - // XScale accumulator register 0 - 7 (they do overlap with wCGR0 - wCGR7) - case dwarf_wCGR0: - return "wCGR0/ACC0"; - case dwarf_wCGR1: - return "wCGR1/ACC1"; - case dwarf_wCGR2: - return "wCGR2/ACC2"; - case dwarf_wCGR3: - return "wCGR3/ACC3"; - case dwarf_wCGR4: - return "wCGR4/ACC4"; - case dwarf_wCGR5: - return "wCGR5/ACC5"; - case dwarf_wCGR6: - return "wCGR6/ACC6"; - case dwarf_wCGR7: - return "wCGR7/ACC7"; - - // Intel wireless MMX data registers 0 - 15 - case dwarf_wR0: - return "wR0"; - case dwarf_wR1: - return "wR1"; - case dwarf_wR2: - return "wR2"; - case dwarf_wR3: - return "wR3"; - case dwarf_wR4: - return "wR4"; - case dwarf_wR5: - return "wR5"; - case dwarf_wR6: - return "wR6"; - case dwarf_wR7: - return "wR7"; - case dwarf_wR8: - return "wR8"; - case dwarf_wR9: - return "wR9"; - case dwarf_wR10: - return "wR10"; - case dwarf_wR11: - return "wR11"; - case dwarf_wR12: - return "wR12"; - case dwarf_wR13: - return "wR13"; - case dwarf_wR14: - return "wR14"; - case dwarf_wR15: - return "wR15"; - - case dwarf_spsr: - return "spsr"; - case dwarf_spsr_fiq: - return "spsr_fiq"; - case dwarf_spsr_irq: - return "spsr_irq"; - case dwarf_spsr_abt: - return "spsr_abt"; - case dwarf_spsr_und: - return "spsr_und"; - case dwarf_spsr_svc: - return "spsr_svc"; - - case dwarf_r8_usr: - return "r8_usr"; - case dwarf_r9_usr: - return "r9_usr"; - case dwarf_r10_usr: - return "r10_usr"; - case dwarf_r11_usr: - return "r11_usr"; - case dwarf_r12_usr: - return "r12_usr"; - case dwarf_r13_usr: - return "r13_usr"; - case dwarf_r14_usr: - return "r14_usr"; - case dwarf_r8_fiq: - return "r8_fiq"; - case dwarf_r9_fiq: - return "r9_fiq"; - case dwarf_r10_fiq: - return "r10_fiq"; - case dwarf_r11_fiq: - return "r11_fiq"; - case dwarf_r12_fiq: - return "r12_fiq"; - case dwarf_r13_fiq: - return "r13_fiq"; - case dwarf_r14_fiq: - return "r14_fiq"; - case dwarf_r13_irq: - return "r13_irq"; - case dwarf_r14_irq: - return "r14_irq"; - case dwarf_r13_abt: - return "r13_abt"; - case dwarf_r14_abt: - return "r14_abt"; - case dwarf_r13_und: - return "r13_und"; - case dwarf_r14_und: - return "r14_und"; - case dwarf_r13_svc: - return "r13_svc"; - case dwarf_r14_svc: - return "r14_svc"; - - // Intel wireless MMX control register in co-processor 0 - 7 - case dwarf_wC0: - return "wC0"; - case dwarf_wC1: - return "wC1"; - case dwarf_wC2: - return "wC2"; - case dwarf_wC3: - return "wC3"; - case dwarf_wC4: - return "wC4"; - case dwarf_wC5: - return "wC5"; - case dwarf_wC6: - return "wC6"; - case dwarf_wC7: - return "wC7"; - - // VFP-v3/Neon - case dwarf_d0: - return "d0"; - case dwarf_d1: - return "d1"; - case dwarf_d2: - return "d2"; - case dwarf_d3: - return "d3"; - case dwarf_d4: - return "d4"; - case dwarf_d5: - return "d5"; - case dwarf_d6: - return "d6"; - case dwarf_d7: - return "d7"; - case dwarf_d8: - return "d8"; - case dwarf_d9: - return "d9"; - case dwarf_d10: - return "d10"; - case dwarf_d11: - return "d11"; - case dwarf_d12: - return "d12"; - case dwarf_d13: - return "d13"; - case dwarf_d14: - return "d14"; - case dwarf_d15: - return "d15"; - case dwarf_d16: - return "d16"; - case dwarf_d17: - return "d17"; - case dwarf_d18: - return "d18"; - case dwarf_d19: - return "d19"; - case dwarf_d20: - return "d20"; - case dwarf_d21: - return "d21"; - case dwarf_d22: - return "d22"; - case dwarf_d23: - return "d23"; - case dwarf_d24: - return "d24"; - case dwarf_d25: - return "d25"; - case dwarf_d26: - return "d26"; - case dwarf_d27: - return "d27"; - case dwarf_d28: - return "d28"; - case dwarf_d29: - return "d29"; - case dwarf_d30: - return "d30"; - case dwarf_d31: - return "d31"; - - // NEON 128-bit vector registers (overlays the d registers) - case dwarf_q0: - return "q0"; - case dwarf_q1: - return "q1"; - case dwarf_q2: - return "q2"; - case dwarf_q3: - return "q3"; - case dwarf_q4: - return "q4"; - case dwarf_q5: - return "q5"; - case dwarf_q6: - return "q6"; - case dwarf_q7: - return "q7"; - case dwarf_q8: - return "q8"; - case dwarf_q9: - return "q9"; - case dwarf_q10: - return "q10"; - case dwarf_q11: - return "q11"; - case dwarf_q12: - return "q12"; - case dwarf_q13: - return "q13"; - case dwarf_q14: - return "q14"; - case dwarf_q15: - return "q15"; - } - return nullptr; -} - -bool GetARMDWARFRegisterInfo(unsigned reg_num, RegisterInfo ®_info) { - ::memset(®_info, 0, sizeof(RegisterInfo)); - ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); - - if (reg_num >= dwarf_q0 && reg_num <= dwarf_q15) { - reg_info.byte_size = 16; - reg_info.format = eFormatVectorOfUInt8; - reg_info.encoding = eEncodingVector; - } - - if (reg_num >= dwarf_d0 && reg_num <= dwarf_d31) { - reg_info.byte_size = 8; - reg_info.format = eFormatFloat; - reg_info.encoding = eEncodingIEEE754; - } else if (reg_num >= dwarf_s0 && reg_num <= dwarf_s31) { - reg_info.byte_size = 4; - reg_info.format = eFormatFloat; - reg_info.encoding = eEncodingIEEE754; - } else if (reg_num >= dwarf_f0 && reg_num <= dwarf_f7) { - reg_info.byte_size = 12; - reg_info.format = eFormatFloat; - reg_info.encoding = eEncodingIEEE754; - } else { - reg_info.byte_size = 4; - reg_info.format = eFormatHex; - reg_info.encoding = eEncodingUint; - } - - reg_info.kinds[eRegisterKindDWARF] = reg_num; - - switch (reg_num) { - case dwarf_r0: - reg_info.name = "r0"; - break; - case dwarf_r1: - reg_info.name = "r1"; - break; - case dwarf_r2: - reg_info.name = "r2"; - break; - case dwarf_r3: - reg_info.name = "r3"; - break; - case dwarf_r4: - reg_info.name = "r4"; - break; - case dwarf_r5: - reg_info.name = "r5"; - break; - case dwarf_r6: - reg_info.name = "r6"; - break; - case dwarf_r7: - reg_info.name = "r7"; - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; - break; - case dwarf_r8: - reg_info.name = "r8"; - break; - case dwarf_r9: - reg_info.name = "r9"; - break; - case dwarf_r10: - reg_info.name = "r10"; - break; - case dwarf_r11: - reg_info.name = "r11"; - break; - case dwarf_r12: - reg_info.name = "r12"; - break; - case dwarf_sp: - reg_info.name = "sp"; - reg_info.alt_name = "r13"; - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; - break; - case dwarf_lr: - reg_info.name = "lr"; - reg_info.alt_name = "r14"; - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; - break; - case dwarf_pc: - reg_info.name = "pc"; - reg_info.alt_name = "r15"; - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; - break; - case dwarf_cpsr: - reg_info.name = "cpsr"; - reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; - break; - - case dwarf_s0: - reg_info.name = "s0"; - break; - case dwarf_s1: - reg_info.name = "s1"; - break; - case dwarf_s2: - reg_info.name = "s2"; - break; - case dwarf_s3: - reg_info.name = "s3"; - break; - case dwarf_s4: - reg_info.name = "s4"; - break; - case dwarf_s5: - reg_info.name = "s5"; - break; - case dwarf_s6: - reg_info.name = "s6"; - break; - case dwarf_s7: - reg_info.name = "s7"; - break; - case dwarf_s8: - reg_info.name = "s8"; - break; - case dwarf_s9: - reg_info.name = "s9"; - break; - case dwarf_s10: - reg_info.name = "s10"; - break; - case dwarf_s11: - reg_info.name = "s11"; - break; - case dwarf_s12: - reg_info.name = "s12"; - break; - case dwarf_s13: - reg_info.name = "s13"; - break; - case dwarf_s14: - reg_info.name = "s14"; - break; - case dwarf_s15: - reg_info.name = "s15"; - break; - case dwarf_s16: - reg_info.name = "s16"; - break; - case dwarf_s17: - reg_info.name = "s17"; - break; - case dwarf_s18: - reg_info.name = "s18"; - break; - case dwarf_s19: - reg_info.name = "s19"; - break; - case dwarf_s20: - reg_info.name = "s20"; - break; - case dwarf_s21: - reg_info.name = "s21"; - break; - case dwarf_s22: - reg_info.name = "s22"; - break; - case dwarf_s23: - reg_info.name = "s23"; - break; - case dwarf_s24: - reg_info.name = "s24"; - break; - case dwarf_s25: - reg_info.name = "s25"; - break; - case dwarf_s26: - reg_info.name = "s26"; - break; - case dwarf_s27: - reg_info.name = "s27"; - break; - case dwarf_s28: - reg_info.name = "s28"; - break; - case dwarf_s29: - reg_info.name = "s29"; - break; - case dwarf_s30: - reg_info.name = "s30"; - break; - case dwarf_s31: - reg_info.name = "s31"; - break; - - // FPA Registers 0-7 - case dwarf_f0: - reg_info.name = "f0"; - break; - case dwarf_f1: - reg_info.name = "f1"; - break; - case dwarf_f2: - reg_info.name = "f2"; - break; - case dwarf_f3: - reg_info.name = "f3"; - break; - case dwarf_f4: - reg_info.name = "f4"; - break; - case dwarf_f5: - reg_info.name = "f5"; - break; - case dwarf_f6: - reg_info.name = "f6"; - break; - case dwarf_f7: - reg_info.name = "f7"; - break; - - // Intel wireless MMX general purpose registers 0 - 7 - // XScale accumulator register 0 - 7 (they do overlap with wCGR0 - wCGR7) - case dwarf_wCGR0: - reg_info.name = "wCGR0/ACC0"; - break; - case dwarf_wCGR1: - reg_info.name = "wCGR1/ACC1"; - break; - case dwarf_wCGR2: - reg_info.name = "wCGR2/ACC2"; - break; - case dwarf_wCGR3: - reg_info.name = "wCGR3/ACC3"; - break; - case dwarf_wCGR4: - reg_info.name = "wCGR4/ACC4"; - break; - case dwarf_wCGR5: - reg_info.name = "wCGR5/ACC5"; - break; - case dwarf_wCGR6: - reg_info.name = "wCGR6/ACC6"; - break; - case dwarf_wCGR7: - reg_info.name = "wCGR7/ACC7"; - break; - - // Intel wireless MMX data registers 0 - 15 - case dwarf_wR0: - reg_info.name = "wR0"; - break; - case dwarf_wR1: - reg_info.name = "wR1"; - break; - case dwarf_wR2: - reg_info.name = "wR2"; - break; - case dwarf_wR3: - reg_info.name = "wR3"; - break; - case dwarf_wR4: - reg_info.name = "wR4"; - break; - case dwarf_wR5: - reg_info.name = "wR5"; - break; - case dwarf_wR6: - reg_info.name = "wR6"; - break; - case dwarf_wR7: - reg_info.name = "wR7"; - break; - case dwarf_wR8: - reg_info.name = "wR8"; - break; - case dwarf_wR9: - reg_info.name = "wR9"; - break; - case dwarf_wR10: - reg_info.name = "wR10"; - break; - case dwarf_wR11: - reg_info.name = "wR11"; - break; - case dwarf_wR12: - reg_info.name = "wR12"; - break; - case dwarf_wR13: - reg_info.name = "wR13"; - break; - case dwarf_wR14: - reg_info.name = "wR14"; - break; - case dwarf_wR15: - reg_info.name = "wR15"; - break; - - case dwarf_spsr: - reg_info.name = "spsr"; - break; - case dwarf_spsr_fiq: - reg_info.name = "spsr_fiq"; - break; - case dwarf_spsr_irq: - reg_info.name = "spsr_irq"; - break; - case dwarf_spsr_abt: - reg_info.name = "spsr_abt"; - break; - case dwarf_spsr_und: - reg_info.name = "spsr_und"; - break; - case dwarf_spsr_svc: - reg_info.name = "spsr_svc"; - break; - - case dwarf_r8_usr: - reg_info.name = "r8_usr"; - break; - case dwarf_r9_usr: - reg_info.name = "r9_usr"; - break; - case dwarf_r10_usr: - reg_info.name = "r10_usr"; - break; - case dwarf_r11_usr: - reg_info.name = "r11_usr"; - break; - case dwarf_r12_usr: - reg_info.name = "r12_usr"; - break; - case dwarf_r13_usr: - reg_info.name = "r13_usr"; - break; - case dwarf_r14_usr: - reg_info.name = "r14_usr"; - break; - case dwarf_r8_fiq: - reg_info.name = "r8_fiq"; - break; - case dwarf_r9_fiq: - reg_info.name = "r9_fiq"; - break; - case dwarf_r10_fiq: - reg_info.name = "r10_fiq"; - break; - case dwarf_r11_fiq: - reg_info.name = "r11_fiq"; - break; - case dwarf_r12_fiq: - reg_info.name = "r12_fiq"; - break; - case dwarf_r13_fiq: - reg_info.name = "r13_fiq"; - break; - case dwarf_r14_fiq: - reg_info.name = "r14_fiq"; - break; - case dwarf_r13_irq: - reg_info.name = "r13_irq"; - break; - case dwarf_r14_irq: - reg_info.name = "r14_irq"; - break; - case dwarf_r13_abt: - reg_info.name = "r13_abt"; - break; - case dwarf_r14_abt: - reg_info.name = "r14_abt"; - break; - case dwarf_r13_und: - reg_info.name = "r13_und"; - break; - case dwarf_r14_und: - reg_info.name = "r14_und"; - break; - case dwarf_r13_svc: - reg_info.name = "r13_svc"; - break; - case dwarf_r14_svc: - reg_info.name = "r14_svc"; - break; - - // Intel wireless MMX control register in co-processor 0 - 7 - case dwarf_wC0: - reg_info.name = "wC0"; - break; - case dwarf_wC1: - reg_info.name = "wC1"; - break; - case dwarf_wC2: - reg_info.name = "wC2"; - break; - case dwarf_wC3: - reg_info.name = "wC3"; - break; - case dwarf_wC4: - reg_info.name = "wC4"; - break; - case dwarf_wC5: - reg_info.name = "wC5"; - break; - case dwarf_wC6: - reg_info.name = "wC6"; - break; - case dwarf_wC7: - reg_info.name = "wC7"; - break; - - // VFP-v3/Neon - case dwarf_d0: - reg_info.name = "d0"; - break; - case dwarf_d1: - reg_info.name = "d1"; - break; - case dwarf_d2: - reg_info.name = "d2"; - break; - case dwarf_d3: - reg_info.name = "d3"; - break; - case dwarf_d4: - reg_info.name = "d4"; - break; - case dwarf_d5: - reg_info.name = "d5"; - break; - case dwarf_d6: - reg_info.name = "d6"; - break; - case dwarf_d7: - reg_info.name = "d7"; - break; - case dwarf_d8: - reg_info.name = "d8"; - break; - case dwarf_d9: - reg_info.name = "d9"; - break; - case dwarf_d10: - reg_info.name = "d10"; - break; - case dwarf_d11: - reg_info.name = "d11"; - break; - case dwarf_d12: - reg_info.name = "d12"; - break; - case dwarf_d13: - reg_info.name = "d13"; - break; - case dwarf_d14: - reg_info.name = "d14"; - break; - case dwarf_d15: - reg_info.name = "d15"; - break; - case dwarf_d16: - reg_info.name = "d16"; - break; - case dwarf_d17: - reg_info.name = "d17"; - break; - case dwarf_d18: - reg_info.name = "d18"; - break; - case dwarf_d19: - reg_info.name = "d19"; - break; - case dwarf_d20: - reg_info.name = "d20"; - break; - case dwarf_d21: - reg_info.name = "d21"; - break; - case dwarf_d22: - reg_info.name = "d22"; - break; - case dwarf_d23: - reg_info.name = "d23"; - break; - case dwarf_d24: - reg_info.name = "d24"; - break; - case dwarf_d25: - reg_info.name = "d25"; - break; - case dwarf_d26: - reg_info.name = "d26"; - break; - case dwarf_d27: - reg_info.name = "d27"; - break; - case dwarf_d28: - reg_info.name = "d28"; - break; - case dwarf_d29: - reg_info.name = "d29"; - break; - case dwarf_d30: - reg_info.name = "d30"; - break; - case dwarf_d31: - reg_info.name = "d31"; - break; - - // NEON 128-bit vector registers (overlays the d registers) - case dwarf_q0: - reg_info.name = "q0"; - break; - case dwarf_q1: - reg_info.name = "q1"; - break; - case dwarf_q2: - reg_info.name = "q2"; - break; - case dwarf_q3: - reg_info.name = "q3"; - break; - case dwarf_q4: - reg_info.name = "q4"; - break; - case dwarf_q5: - reg_info.name = "q5"; - break; - case dwarf_q6: - reg_info.name = "q6"; - break; - case dwarf_q7: - reg_info.name = "q7"; - break; - case dwarf_q8: - reg_info.name = "q8"; - break; - case dwarf_q9: - reg_info.name = "q9"; - break; - case dwarf_q10: - reg_info.name = "q10"; - break; - case dwarf_q11: - reg_info.name = "q11"; - break; - case dwarf_q12: - reg_info.name = "q12"; - break; - case dwarf_q13: - reg_info.name = "q13"; - break; - case dwarf_q14: - reg_info.name = "q14"; - break; - case dwarf_q15: - reg_info.name = "q15"; - break; - - default: - return false; - } - return true; -} diff --git a/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.h b/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.h index 9b226c1..ab91d8c 100644 --- a/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.h +++ b/contrib/llvm/tools/lldb/source/Utility/ARM_DWARF_Registers.h @@ -205,9 +205,4 @@ enum { dwarf_q15 }; -const char *GetARMDWARFRegisterName(unsigned reg_num); - -bool GetARMDWARFRegisterInfo(unsigned reg_num, - lldb_private::RegisterInfo ®_info); - #endif // utility_ARM_DWARF_Registers_h_ diff --git a/contrib/llvm/tools/lldb/source/Utility/Baton.cpp b/contrib/llvm/tools/lldb/source/Utility/Baton.cpp new file mode 100644 index 0000000..786be2f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Baton.cpp @@ -0,0 +1,13 @@ +//===-- Baton.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Baton.h" + +void lldb_private::UntypedBaton::GetDescription( + Stream *s, lldb::DescriptionLevel level) const {} diff --git a/contrib/llvm/tools/lldb/source/Utility/Connection.cpp b/contrib/llvm/tools/lldb/source/Utility/Connection.cpp new file mode 100644 index 0000000..9f6114f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Connection.cpp @@ -0,0 +1,14 @@ +//===-- Connection.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Connection.h" + +using namespace lldb_private; + +Connection::~Connection() = default; diff --git a/contrib/llvm/tools/lldb/source/Utility/ConstString.cpp b/contrib/llvm/tools/lldb/source/Utility/ConstString.cpp new file mode 100644 index 0000000..49cf8a6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/ConstString.cpp @@ -0,0 +1,340 @@ +//===-- ConstString.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/ConstString.h" + +#include "lldb/Utility/Stream.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" // for iterator_facade_base +#include "llvm/Support/Allocator.h" // for BumpPtrAllocator +#include "llvm/Support/FormatProviders.h" // for format_provider +#include "llvm/Support/RWMutex.h" +#include "llvm/Support/Threading.h" + +#include <algorithm> // for min +#include <array> +#include <utility> // for make_pair, pair + +#include <inttypes.h> // for PRIu64 +#include <stdint.h> // for uint8_t, uint32_t, uint64_t +#include <string.h> // for size_t, strlen + +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; + } + + bool SetMangledCounterparts(const char *key_ccstr, const char *value_ccstr) { + if (key_ccstr != nullptr && value_ccstr != nullptr) { + { + const uint8_t h = hash(llvm::StringRef(key_ccstr)); + llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); + GetStringMapEntryFromKeyData(key_ccstr).setValue(value_ccstr); + } + { + const uint8_t h = hash(llvm::StringRef(value_ccstr)); + llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); + GetStringMapEntryFromKeyData(value_ccstr).setValue(key_ccstr); + } + return true; + } + return false; + } + + 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(const char *demangled_cstr, + const char *mangled_ccstr) { + if (demangled_cstr != nullptr) { + const char *demangled_ccstr = nullptr; + + { + llvm::StringRef string_ref(demangled_cstr); + const uint8_t h = hash(string_ref); + llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); + + // Make string pool entry with the mangled counterpart already set + StringPoolEntryType &entry = + *m_string_pools[h] + .m_string_map.insert(std::make_pair(string_ref, mangled_ccstr)) + .first; + + // 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; + } + return nullptr; + } + + const char *GetConstTrimmedCStringWithLength(const char *cstr, + size_t cstr_len) { + if (cstr != nullptr) { + const size_t trimmed_len = std::min<size_t>(strlen(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::HashString(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().GetConstCStringWithLength(s.data(), s.size())) {} + +bool ConstString::operator<(const 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, const 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(const ConstString &lhs, const 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(const ConstString &lhs, const 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::SetCStringWithMangledCounterpart(const char *demangled, + const 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/contrib/llvm/tools/lldb/source/Utility/ConvertEnum.cpp b/contrib/llvm/tools/lldb/source/Utility/ConvertEnum.cpp deleted file mode 100644 index bb0484e..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/ConvertEnum.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//===-- ConvertEnum.cpp -----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "lldb/Utility/ConvertEnum.h" - -using namespace lldb; -using namespace lldb_private; - -const char *lldb_private::GetVoteAsCString(Vote vote) { - switch (vote) { - case eVoteNo: - return "no"; - case eVoteNoOpinion: - return "no opinion"; - case eVoteYes: - return "yes"; - } - return "invalid"; -} - -const char *lldb_private::GetSectionTypeAsCString(lldb::SectionType sect_type) { - switch (sect_type) { - case eSectionTypeInvalid: - return "invalid"; - case eSectionTypeCode: - return "code"; - case eSectionTypeContainer: - return "container"; - case eSectionTypeData: - return "data"; - case eSectionTypeDataCString: - return "data-cstr"; - case eSectionTypeDataCStringPointers: - return "data-cstr-ptr"; - case eSectionTypeDataSymbolAddress: - return "data-symbol-addr"; - case eSectionTypeData4: - return "data-4-byte"; - case eSectionTypeData8: - return "data-8-byte"; - case eSectionTypeData16: - return "data-16-byte"; - case eSectionTypeDataPointers: - return "data-ptrs"; - case eSectionTypeDebug: - return "debug"; - case eSectionTypeZeroFill: - return "zero-fill"; - case eSectionTypeDataObjCMessageRefs: - return "objc-message-refs"; - case eSectionTypeDataObjCCFStrings: - return "objc-cfstrings"; - case eSectionTypeDWARFDebugAbbrev: - return "dwarf-abbrev"; - case eSectionTypeDWARFDebugAddr: - return "dwarf-addr"; - case eSectionTypeDWARFDebugAranges: - return "dwarf-aranges"; - case eSectionTypeDWARFDebugFrame: - return "dwarf-frame"; - case eSectionTypeDWARFDebugInfo: - return "dwarf-info"; - case eSectionTypeDWARFDebugLine: - return "dwarf-line"; - case eSectionTypeDWARFDebugLoc: - return "dwarf-loc"; - case eSectionTypeDWARFDebugMacInfo: - return "dwarf-macinfo"; - case eSectionTypeDWARFDebugMacro: - return "dwarf-macro"; - case eSectionTypeDWARFDebugPubNames: - return "dwarf-pubnames"; - case eSectionTypeDWARFDebugPubTypes: - return "dwarf-pubtypes"; - case eSectionTypeDWARFDebugRanges: - return "dwarf-ranges"; - case eSectionTypeDWARFDebugStr: - return "dwarf-str"; - case eSectionTypeDWARFDebugStrOffsets: - return "dwarf-str-offsets"; - case eSectionTypeELFSymbolTable: - return "elf-symbol-table"; - case eSectionTypeELFDynamicSymbols: - return "elf-dynamic-symbols"; - case eSectionTypeELFRelocationEntries: - return "elf-relocation-entries"; - case eSectionTypeELFDynamicLinkInfo: - return "elf-dynamic-link-info"; - case eSectionTypeDWARFAppleNames: - return "apple-names"; - case eSectionTypeDWARFAppleTypes: - return "apple-types"; - case eSectionTypeDWARFAppleNamespaces: - return "apple-namespaces"; - case eSectionTypeDWARFAppleObjC: - return "apple-objc"; - case eSectionTypeEHFrame: - return "eh-frame"; - case eSectionTypeARMexidx: - return "ARM.exidx"; - case eSectionTypeARMextab: - return "ARM.extab"; - case eSectionTypeCompactUnwind: - return "compact-unwind"; - case eSectionTypeGoSymtab: - return "go-symtab"; - case eSectionTypeAbsoluteAddress: - return "absolute"; - case eSectionTypeOther: - return "regular"; - } - return "unknown"; -} diff --git a/contrib/llvm/tools/lldb/source/Utility/DataBufferHeap.cpp b/contrib/llvm/tools/lldb/source/Utility/DataBufferHeap.cpp new file mode 100644 index 0000000..aa1c3d3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/DataBufferHeap.cpp @@ -0,0 +1,94 @@ +//===-- DataBufferHeap.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/DataBufferHeap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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 = (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(), (const uint8_t *)src, + (const uint8_t *)src + src_len); +} + +void DataBufferHeap::Clear() { + buffer_t empty; + m_data.swap(empty); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/DataBufferLLVM.cpp b/contrib/llvm/tools/lldb/source/Utility/DataBufferLLVM.cpp new file mode 100644 index 0000000..bebcafb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/DataBufferLLVM.cpp @@ -0,0 +1,70 @@ +//===--- DataBufferLLVM.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/DataBufferLLVM.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <assert.h> // for assert +#include <type_traits> // for move + +using namespace lldb_private; + +DataBufferLLVM::DataBufferLLVM(std::unique_ptr<llvm::MemoryBuffer> MemBuffer) + : Buffer(std::move(MemBuffer)) { + assert(Buffer != nullptr && + "Cannot construct a DataBufferLLVM with a null buffer"); +} + +DataBufferLLVM::~DataBufferLLVM() {} + +std::shared_ptr<DataBufferLLVM> +DataBufferLLVM::CreateSliceFromPath(const llvm::Twine &Path, uint64_t Size, + uint64_t Offset, bool Private) { + // If the file resides non-locally, pass the volatile flag so that we don't + // mmap it. + if (!Private) + Private = !llvm::sys::fs::is_local(Path); + + auto Buffer = llvm::MemoryBuffer::getFileSlice(Path, Size, Offset, Private); + if (!Buffer) + return nullptr; + return std::shared_ptr<DataBufferLLVM>( + new DataBufferLLVM(std::move(*Buffer))); +} + +std::shared_ptr<DataBufferLLVM> +DataBufferLLVM::CreateFromPath(const llvm::Twine &Path, bool NullTerminate, bool Private) { + // If the file resides non-locally, pass the volatile flag so that we don't + // mmap it. + if (!Private) + Private = !llvm::sys::fs::is_local(Path); + + auto Buffer = llvm::MemoryBuffer::getFile(Path, -1, NullTerminate, Private); + if (!Buffer) + return nullptr; + return std::shared_ptr<DataBufferLLVM>( + new DataBufferLLVM(std::move(*Buffer))); +} + +uint8_t *DataBufferLLVM::GetBytes() { + return const_cast<uint8_t *>(GetBuffer()); +} + +const uint8_t *DataBufferLLVM::GetBytes() const { return GetBuffer(); } + +lldb::offset_t DataBufferLLVM::GetByteSize() const { + return Buffer->getBufferSize(); +} + +const uint8_t *DataBufferLLVM::GetBuffer() const { + return reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/DataEncoder.cpp b/contrib/llvm/tools/lldb/source/Utility/DataEncoder.cpp new file mode 100644 index 0000000..f7ce468 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/DataEncoder.cpp @@ -0,0 +1,285 @@ +//===-- DataEncoder.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/DataEncoder.h" + +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Endian.h" + +#include "llvm/Support/ErrorHandling.h" // for llvm_unreachable +#include "llvm/Support/MathExtras.h" + +#include <cassert> +#include <cstddef> + +#include <string.h> + +using namespace lldb; +using namespace lldb_private; + +static inline void WriteInt16(unsigned char *ptr, unsigned offset, + uint16_t value) { + *(uint16_t *)(ptr + offset) = value; +} + +static inline void WriteInt32(unsigned char *ptr, unsigned offset, + uint32_t value) { + *(uint32_t *)(ptr + offset) = value; +} + +static inline void WriteInt64(unsigned char *ptr, unsigned offset, + uint64_t value) { + *(uint64_t *)(ptr + offset) = value; +} + +static inline void WriteSwappedInt16(unsigned char *ptr, unsigned offset, + uint16_t value) { + *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); +} + +static inline void WriteSwappedInt32(unsigned char *ptr, unsigned offset, + uint32_t value) { + *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); +} + +static inline void WriteSwappedInt64(unsigned char *ptr, unsigned offset, + uint64_t value) { + *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); +} + +//---------------------------------------------------------------------- +// 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((uint8_t *)data), m_end((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 = (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()) + WriteSwappedInt16(m_start, offset, value); + else + WriteInt16(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()) + WriteSwappedInt32(m_start, offset, value); + else + WriteInt32(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()) + WriteSwappedInt64(m_start, offset, value); + else + WriteInt64(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/contrib/llvm/tools/lldb/source/Utility/DataExtractor.cpp b/contrib/llvm/tools/lldb/source/Utility/DataExtractor.cpp new file mode 100644 index 0000000..008aff2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/DataExtractor.cpp @@ -0,0 +1,1245 @@ +//===-- DataExtractor.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/DataExtractor.h" + +#include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS +#include "lldb/lldb-enumerations.h" // for ByteOrder::eByteOrderBig +#include "lldb/lldb-forward.h" // for DataBufferSP +#include "lldb/lldb-types.h" // for offset_t + +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.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> // for min +#include <array> // for array +#include <cassert> +#include <cstdint> // for uint8_t, uint32_t, uint64_t +#include <string> + +#include <ctype.h> // for isprint +#include <inttypes.h> // for PRIx64, PRId64 +#include <string.h> // for memcpy, memset, memchr + +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); +} + +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) { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(addr_size == 4 || addr_size == 8); +#endif +} + +//---------------------------------------------------------------------- +// 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) { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(addr_size == 4 || addr_size == 8); +#endif + 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) { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif + 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) { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif +} + +//---------------------------------------------------------------------- +// 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; +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif + // 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 = (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 = (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 = (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 = (const uint16_t *)GetData(offset_ptr, src_size); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint16_t *dst_pos = (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 = (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 = (const uint32_t *)GetData(offset_ptr, src_size); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint32_t *dst_pos = (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 = (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 = (const uint64_t *)GetData(offset_ptr, src_size); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + uint64_t *dst_pos = (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; +} + +//---------------------------------------------------------------------- +// 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 between 1 and 4 since the return value is only 32 bits +// wide. Any "byte_size" values less than 1 or greater than 4 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t DataExtractor::GetMaxU32(offset_t *offset_ptr, + size_t byte_size) const { + switch (byte_size) { + case 1: + return GetU8(offset_ptr); + break; + case 2: + return GetU16(offset_ptr); + break; + case 4: + return GetU32(offset_ptr); + break; + default: + assert(false && "GetMaxU32 unhandled case!"); + break; + } + return 0; +} + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +uint64_t DataExtractor::GetMaxU64(offset_t *offset_ptr, size_t size) const { + switch (size) { + case 1: + return GetU8(offset_ptr); + break; + case 2: + return GetU16(offset_ptr); + break; + case 4: + return GetU32(offset_ptr); + break; + case 8: + return GetU64(offset_ptr); + break; + default: + assert(false && "GetMax64 unhandled case!"); + break; + } + return 0; +} + +uint64_t DataExtractor::GetMaxU64_unchecked(offset_t *offset_ptr, + size_t size) const { + switch (size) { + case 1: + return GetU8_unchecked(offset_ptr); + break; + case 2: + return GetU16_unchecked(offset_ptr); + break; + case 4: + return GetU32_unchecked(offset_ptr); + break; + case 8: + return GetU64_unchecked(offset_ptr); + break; + default: + assert(false && "GetMax64 unhandled case!"); + break; + } + return 0; +} + +int64_t DataExtractor::GetMaxS64(offset_t *offset_ptr, size_t size) const { + switch (size) { + case 1: + return (int8_t)GetU8(offset_ptr); + break; + case 2: + return (int16_t)GetU16(offset_ptr); + break; + case 4: + return (int32_t)GetU32(offset_ptr); + break; + case 8: + return (int64_t)GetU64(offset_ptr); + break; + default: + assert(false && "GetMax64 unhandled case!"); + break; + } + return 0; +} + +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 = (((uint64_t)1) << bitfield_bit_size) - 1; + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (((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 = (const float_type *)GetData(offset_ptr, src_size); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (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 = (const float_type *)GetData(offset_ptr, src_size); + if (src) { + if (m_byte_order != endian::InlHostByteOrder()) { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (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 { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif + return GetMaxU64(offset_ptr, m_addr_size); +} + +uint64_t DataExtractor::GetAddress_unchecked(offset_t *offset_ptr) const { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif + 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 { +#ifdef LLDB_CONFIGURATION_DEBUG + assert(m_addr_size == 4 || m_addr_size == 8); +#endif + 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) + ((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 = (uint8_t *)dst_void_ptr; + const uint8_t *src = (const uint8_t *)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 *cstr = (const char *)PeekData(*offset_ptr, 1); + if (cstr) { + const char *cstr_end = cstr; + const char *end = (const char *)m_end; + while (cstr_end < end && *cstr_end) + ++cstr_end; + + // Now we are either at the end of the data or we point to the + // NULL C string terminator with cstr_end... + if (*cstr_end == '\0') { + // Advance the offset with one extra byte for the NULL terminator + *offset_ptr += (cstr_end - cstr + 1); + return cstr; + } + + // We reached the end of the data without finding a NULL C string + // terminator. Fall through and return nullptr otherwise anyone that + // would have used the result as a C string can wander into + // unknown memory... + } + return nullptr; +} + +//---------------------------------------------------------------------- +// 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 = (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 (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 = (const uint8_t *)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 |= (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 = (const uint8_t *)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 |= (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 = (const uint8_t *)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 ":", + (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 +} + +//---------------------------------------------------------------------- +// DumpUUID +// +// Dump out a UUID starting at 'offset' bytes into the buffer +//---------------------------------------------------------------------- +void DataExtractor::DumpUUID(Stream *s, offset_t offset) const { + if (s) { + const uint8_t *uuid_data = PeekData(offset, 16); + if (uuid_data) { + lldb_private::UUID uuid(uuid_data, 16); + uuid.Dump(s); + } else { + s->Printf("<not enough data for UUID at offset 0x%8.8" PRIx64 ">", + offset); + } + } +} + +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/contrib/llvm/tools/lldb/source/Utility/FastDemangle.cpp b/contrib/llvm/tools/lldb/source/Utility/FastDemangle.cpp new file mode 100644 index 0000000..90326c5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/FastDemangle.cpp @@ -0,0 +1,2397 @@ +//===-- FastDemangle.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/FastDemangle.h" + +#include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH + +#include <functional> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +//#define DEBUG_FAILURES 1 +//#define DEBUG_SUBSTITUTIONS 1 +//#define DEBUG_TEMPLATE_ARGS 1 +//#define DEBUG_HIGHWATER 1 +//#define DEBUG_REORDERING 1 + +namespace { + +/// @brief Represents the collection of qualifiers on a type + +enum Qualifiers { + QualifierNone = 0, + QualifierConst = 1, + QualifierRestrict = 2, + QualifierVolatile = 4, + QualifierReference = 8, + QualifierRValueReference = 16, + QualifierPointer = 32 +}; + +/// @brief Categorizes the recognized operators + +enum class OperatorKind { + Unary, + Postfix, + Binary, + Ternary, + Other, + ConversionOperator, + Vendor, + NoMatch +}; + +/// @brief Represents one of the recognized two-character operator +/// abbreviations used when parsing operators as names and expressions + +struct Operator { + const char *name; + OperatorKind kind; +}; + +/// @brief Represents a range of characters in the output buffer, typically for +/// use with RewriteRange() + +struct BufferRange { + int offset; + int length; +}; + +/// @brief Transient state required while parsing a name + +struct NameState { + bool parse_function_params; + bool is_last_generic; + bool has_no_return_type; + BufferRange last_name_range; +}; + +/// @brief LLDB's fast C++ demangler +/// +/// This is an incomplete implementation designed to speed up the demangling +/// process that is often a bottleneck when LLDB stops a process for the first +/// time. Where the implementation doesn't know how to demangle a symbol it +/// fails gracefully to allow the caller to fall back to the existing demangler. +/// +/// Over time the full mangling spec should be supported without compromising +/// performance for the most common cases. + +class SymbolDemangler { +public: + //---------------------------------------------------- + // Public API + //---------------------------------------------------- + + /// @brief Create a SymbolDemangler + /// + /// The newly created demangler allocates and owns scratch memory sufficient + /// for demangling typical symbols. Additional memory will be allocated if + /// needed and managed by the demangler instance. + + SymbolDemangler() { + m_buffer = (char *)malloc(8192); + m_buffer_end = m_buffer + 8192; + m_owns_buffer = true; + + m_rewrite_ranges = (BufferRange *)malloc(128 * sizeof(BufferRange)); + m_rewrite_ranges_size = 128; + m_owns_m_rewrite_ranges = true; + } + + /// @brief Create a SymbolDemangler that uses provided scratch memory + /// + /// The provided memory is not owned by the demangler. It will be + /// overwritten during calls to GetDemangledCopy() but can be used for + /// other purposes between calls. The provided memory will not be freed + /// when this instance is destroyed. + /// + /// If demangling a symbol requires additional space it will be allocated + /// and managed by the demangler instance. + /// + /// @param storage_ptr Valid pointer to at least storage_size bytes of + /// space that the SymbolDemangler can use during demangling + /// + /// @param storage_size Number of bytes of space available scratch memory + /// referenced by storage_ptr + + SymbolDemangler(void *storage_ptr, size_t storage_size, + std::function<void(const char *)> builtins_hook = nullptr) + : m_builtins_hook(builtins_hook) { + // Use up to 1/8th of the provided space for rewrite ranges + m_rewrite_ranges_size = (storage_size >> 3) / sizeof(BufferRange); + m_rewrite_ranges = (BufferRange *)storage_ptr; + m_owns_m_rewrite_ranges = false; + + // Use the rest for the character buffer + m_buffer = + (char *)storage_ptr + m_rewrite_ranges_size * sizeof(BufferRange); + m_buffer_end = (const char *)storage_ptr + storage_size; + m_owns_buffer = false; + } + + /// @brief Destroys the SymbolDemangler and deallocates any scratch + /// memory that it owns + + ~SymbolDemangler() { + if (m_owns_buffer) + free(m_buffer); + if (m_owns_m_rewrite_ranges) + free(m_rewrite_ranges); + } + +#ifdef DEBUG_HIGHWATER + int highwater_store = 0; + int highwater_buffer = 0; +#endif + + /// @brief Parses the provided mangled name and returns a newly allocated + /// demangling + /// + /// @param mangled_name Valid null-terminated C++ mangled name following + /// the Itanium C++ ABI mangling specification as implemented by Clang + /// + /// @result Newly allocated null-terminated demangled name when demangling + /// is successful, and nullptr when demangling fails. The caller is + /// responsible for freeing the allocated memory. + + char *GetDemangledCopy(const char *mangled_name, + long mangled_name_length = 0) { + if (!ParseMangling(mangled_name, mangled_name_length)) + return nullptr; + +#ifdef DEBUG_HIGHWATER + int rewrite_count = m_next_substitute_index + + (m_rewrite_ranges_size - 1 - m_next_template_arg_index); + int buffer_size = (int)(m_write_ptr - m_buffer); + if (rewrite_count > highwater_store) + highwater_store = rewrite_count; + if (buffer_size > highwater_buffer) + highwater_buffer = buffer_size; +#endif + + int length = (int)(m_write_ptr - m_buffer); + char *copy = (char *)malloc(length + 1); + memcpy(copy, m_buffer, length); + copy[length] = '\0'; + return copy; + } + +private: + //---------------------------------------------------- + // Grow methods + // + // Manage the storage used during demangling + //---------------------------------------------------- + + void GrowBuffer(long min_growth = 0) { + // By default, double the size of the buffer + long growth = m_buffer_end - m_buffer; + + // Avoid growing by more than 1MB at a time + if (growth > 1 << 20) + growth = 1 << 20; + + // ... but never grow by less than requested, + // or 1K, whichever is greater + if (min_growth < 1024) + min_growth = 1024; + if (growth < min_growth) + growth = min_growth; + + // Allocate the new m_buffer and migrate content + long new_size = (m_buffer_end - m_buffer) + growth; + char *new_buffer = (char *)malloc(new_size); + memcpy(new_buffer, m_buffer, m_write_ptr - m_buffer); + if (m_owns_buffer) + free(m_buffer); + m_owns_buffer = true; + + // Update references to the new buffer + m_write_ptr = new_buffer + (m_write_ptr - m_buffer); + m_buffer = new_buffer; + m_buffer_end = m_buffer + new_size; + } + + void GrowRewriteRanges() { + // By default, double the size of the array + int growth = m_rewrite_ranges_size; + + // Apply reasonable minimum and maximum sizes for growth + if (growth > 128) + growth = 128; + if (growth < 16) + growth = 16; + + // Allocate the new array and migrate content + int bytes = (m_rewrite_ranges_size + growth) * sizeof(BufferRange); + BufferRange *new_ranges = (BufferRange *)malloc(bytes); + for (int index = 0; index < m_next_substitute_index; index++) { + new_ranges[index] = m_rewrite_ranges[index]; + } + for (int index = m_rewrite_ranges_size - 1; + index > m_next_template_arg_index; index--) { + new_ranges[index + growth] = m_rewrite_ranges[index]; + } + if (m_owns_m_rewrite_ranges) + free(m_rewrite_ranges); + m_owns_m_rewrite_ranges = true; + + // Update references to the new array + m_rewrite_ranges = new_ranges; + m_rewrite_ranges_size += growth; + m_next_template_arg_index += growth; + } + + //---------------------------------------------------- + // Range and state management + //---------------------------------------------------- + + int GetStartCookie() { return (int)(m_write_ptr - m_buffer); } + + BufferRange EndRange(int start_cookie) { + return {start_cookie, (int)(m_write_ptr - (m_buffer + start_cookie))}; + } + + void ReorderRange(BufferRange source_range, int insertion_point_cookie) { + // Ensure there's room the preserve the source range + if (m_write_ptr + source_range.length > m_buffer_end) { + GrowBuffer(m_write_ptr + source_range.length - m_buffer_end); + } + + // Reorder the content + memcpy(m_write_ptr, m_buffer + source_range.offset, source_range.length); + memmove(m_buffer + insertion_point_cookie + source_range.length, + m_buffer + insertion_point_cookie, + source_range.offset - insertion_point_cookie); + memcpy(m_buffer + insertion_point_cookie, m_write_ptr, source_range.length); + + // Fix up rewritable ranges, covering both substitutions and templates + int index = 0; + while (true) { + if (index == m_next_substitute_index) + index = m_next_template_arg_index + 1; + if (index == m_rewrite_ranges_size) + break; + + // Affected ranges are either shuffled forward when after the + // insertion but before the source, or backward when inside the + // source + int candidate_offset = m_rewrite_ranges[index].offset; + if (candidate_offset >= insertion_point_cookie) { + if (candidate_offset < source_range.offset) { + m_rewrite_ranges[index].offset += source_range.length; + } else if (candidate_offset >= source_range.offset) { + m_rewrite_ranges[index].offset -= + (source_range.offset - insertion_point_cookie); + } + } + ++index; + } + } + + void EndSubstitution(int start_cookie) { + if (m_next_substitute_index == m_next_template_arg_index) + GrowRewriteRanges(); + + int index = m_next_substitute_index++; + m_rewrite_ranges[index] = EndRange(start_cookie); +#ifdef DEBUG_SUBSTITUTIONS + printf("Saved substitution # %d = %.*s\n", index, + m_rewrite_ranges[index].length, m_buffer + start_cookie); +#endif + } + + void EndTemplateArg(int start_cookie) { + if (m_next_substitute_index == m_next_template_arg_index) + GrowRewriteRanges(); + + int index = m_next_template_arg_index--; + m_rewrite_ranges[index] = EndRange(start_cookie); +#ifdef DEBUG_TEMPLATE_ARGS + printf("Saved template arg # %d = %.*s\n", + m_rewrite_ranges_size - index - 1, m_rewrite_ranges[index].length, + m_buffer + start_cookie); +#endif + } + + void ResetTemplateArgs() { + // TODO: this works, but is it the right thing to do? + // Should we push/pop somehow at the call sites? + m_next_template_arg_index = m_rewrite_ranges_size - 1; + } + + //---------------------------------------------------- + // Write methods + // + // Appends content to the existing output buffer + //---------------------------------------------------- + + void Write(char character) { + if (m_write_ptr == m_buffer_end) + GrowBuffer(); + *m_write_ptr++ = character; + } + + void Write(const char *content) { Write(content, strlen(content)); } + + void Write(const char *content, long content_length) { + char *end_m_write_ptr = m_write_ptr + content_length; + if (end_m_write_ptr > m_buffer_end) { + if (content >= m_buffer && content < m_buffer_end) { + long offset = content - m_buffer; + GrowBuffer(end_m_write_ptr - m_buffer_end); + content = m_buffer + offset; + } else { + GrowBuffer(end_m_write_ptr - m_buffer_end); + } + end_m_write_ptr = m_write_ptr + content_length; + } + memcpy(m_write_ptr, content, content_length); + m_write_ptr = end_m_write_ptr; + } +#define WRITE(x) Write(x, sizeof(x) - 1) + + void WriteTemplateStart() { Write('<'); } + + void WriteTemplateEnd() { + // Put a space between terminal > characters when nesting templates + if (m_write_ptr != m_buffer && *(m_write_ptr - 1) == '>') + WRITE(" >"); + else + Write('>'); + } + + void WriteCommaSpace() { WRITE(", "); } + + void WriteNamespaceSeparator() { WRITE("::"); } + + void WriteStdPrefix() { WRITE("std::"); } + + void WriteQualifiers(int qualifiers, bool space_before_reference = true) { + if (qualifiers & QualifierPointer) + Write('*'); + if (qualifiers & QualifierConst) + WRITE(" const"); + if (qualifiers & QualifierVolatile) + WRITE(" volatile"); + if (qualifiers & QualifierRestrict) + WRITE(" restrict"); + if (qualifiers & QualifierReference) { + if (space_before_reference) + WRITE(" &"); + else + Write('&'); + } + if (qualifiers & QualifierRValueReference) { + if (space_before_reference) + WRITE(" &&"); + else + WRITE("&&"); + } + } + + //---------------------------------------------------- + // Rewrite methods + // + // Write another copy of content already present + // earlier in the output buffer + //---------------------------------------------------- + + void RewriteRange(BufferRange range) { + Write(m_buffer + range.offset, range.length); + } + + bool RewriteSubstitution(int index) { + if (index < 0 || index >= m_next_substitute_index) { +#ifdef DEBUG_FAILURES + printf("*** Invalid substitution #%d\n", index); +#endif + return false; + } + RewriteRange(m_rewrite_ranges[index]); + return true; + } + + bool RewriteTemplateArg(int template_index) { + int index = m_rewrite_ranges_size - 1 - template_index; + if (template_index < 0 || index <= m_next_template_arg_index) { +#ifdef DEBUG_FAILURES + printf("*** Invalid template arg reference #%d\n", template_index); +#endif + return false; + } + RewriteRange(m_rewrite_ranges[index]); + return true; + } + + //---------------------------------------------------- + // TryParse methods + // + // Provide information with return values instead of + // writing to the output buffer + // + // Values indicating failure guarantee that the pre- + // call m_read_ptr is unchanged + //---------------------------------------------------- + + int TryParseNumber() { + unsigned char digit = *m_read_ptr - '0'; + if (digit > 9) + return -1; + + int count = digit; + while (true) { + digit = *++m_read_ptr - '0'; + if (digit > 9) + break; + + count = count * 10 + digit; + } + return count; + } + + int TryParseBase36Number() { + char digit = *m_read_ptr; + int count; + if (digit >= '0' && digit <= '9') + count = digit -= '0'; + else if (digit >= 'A' && digit <= 'Z') + count = digit -= ('A' - 10); + else + return -1; + + while (true) { + digit = *++m_read_ptr; + if (digit >= '0' && digit <= '9') + digit -= '0'; + else if (digit >= 'A' && digit <= 'Z') + digit -= ('A' - 10); + else + break; + + count = count * 36 + digit; + } + return count; + } + + // <builtin-type> ::= v # void + // ::= w # wchar_t + // ::= b # bool + // ::= c # char + // ::= a # signed char + // ::= h # unsigned char + // ::= s # short + // ::= t # unsigned short + // ::= i # int + // ::= j # unsigned int + // ::= l # long + // ::= m # unsigned long + // ::= x # long long, __int64 + // ::= y # unsigned long long, __int64 + // ::= n # __int128 + // ::= o # unsigned __int128 + // ::= f # float + // ::= d # double + // ::= e # long double, __float80 + // ::= g # __float128 + // ::= z # ellipsis + // ::= Dd # IEEE 754r decimal floating point (64 bits) + // ::= De # IEEE 754r decimal floating point (128 bits) + // ::= Df # IEEE 754r decimal floating point (32 bits) + // ::= Dh # IEEE 754r half-precision floating point (16 bits) + // ::= Di # char32_t + // ::= Ds # char16_t + // ::= Da # auto (in dependent new-expressions) + // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) + // ::= u <source-name> # vendor extended type + + const char *TryParseBuiltinType() { + if (m_builtins_hook) + m_builtins_hook(m_read_ptr); + + switch (*m_read_ptr++) { + case 'v': + return "void"; + case 'w': + return "wchar_t"; + case 'b': + return "bool"; + case 'c': + return "char"; + case 'a': + return "signed char"; + case 'h': + return "unsigned char"; + case 's': + return "short"; + case 't': + return "unsigned short"; + case 'i': + return "int"; + case 'j': + return "unsigned int"; + case 'l': + return "long"; + case 'm': + return "unsigned long"; + case 'x': + return "long long"; + case 'y': + return "unsigned long long"; + case 'n': + return "__int128"; + case 'o': + return "unsigned __int128"; + case 'f': + return "float"; + case 'd': + return "double"; + case 'e': + return "long double"; + case 'g': + return "__float128"; + case 'z': + return "..."; + case 'D': { + switch (*m_read_ptr++) { + case 'd': + return "decimal64"; + case 'e': + return "decimal128"; + case 'f': + return "decimal32"; + case 'h': + return "decimal16"; + case 'i': + return "char32_t"; + case 's': + return "char16_t"; + case 'a': + return "auto"; + case 'c': + return "decltype(auto)"; + case 'n': + return "std::nullptr_t"; + default: + --m_read_ptr; + } + } + } + --m_read_ptr; + return nullptr; + } + + // <operator-name> + // ::= aa # && + // ::= ad # & (unary) + // ::= an # & + // ::= aN # &= + // ::= aS # = + // ::= cl # () + // ::= cm # , + // ::= co # ~ + // ::= da # delete[] + // ::= de # * (unary) + // ::= dl # delete + // ::= dv # / + // ::= dV # /= + // ::= eo # ^ + // ::= eO # ^= + // ::= eq # == + // ::= ge # >= + // ::= gt # > + // ::= ix # [] + // ::= le # <= + // ::= ls # << + // ::= lS # <<= + // ::= lt # < + // ::= mi # - + // ::= mI # -= + // ::= ml # * + // ::= mL # *= + // ::= mm # -- (postfix in <expression> context) + // ::= na # new[] + // ::= ne # != + // ::= ng # - (unary) + // ::= nt # ! + // ::= nw # new + // ::= oo # || + // ::= or # | + // ::= oR # |= + // ::= pm # ->* + // ::= pl # + + // ::= pL # += + // ::= pp # ++ (postfix in <expression> context) + // ::= ps # + (unary) + // ::= pt # -> + // ::= qu # ? + // ::= rm # % + // ::= rM # %= + // ::= rs # >> + // ::= rS # >>= + // ::= cv <type> # (cast) + // ::= v <digit> <source-name> # vendor extended + // operator + + Operator TryParseOperator() { + switch (*m_read_ptr++) { + case 'a': + switch (*m_read_ptr++) { + case 'a': + return {"&&", OperatorKind::Binary}; + case 'd': + return {"&", OperatorKind::Unary}; + case 'n': + return {"&", OperatorKind::Binary}; + case 'N': + return {"&=", OperatorKind::Binary}; + case 'S': + return {"=", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'c': + switch (*m_read_ptr++) { + case 'l': + return {"()", OperatorKind::Other}; + case 'm': + return {",", OperatorKind::Other}; + case 'o': + return {"~", OperatorKind::Unary}; + case 'v': + return {nullptr, OperatorKind::ConversionOperator}; + } + --m_read_ptr; + break; + case 'd': + switch (*m_read_ptr++) { + case 'a': + return {" delete[]", OperatorKind::Other}; + case 'e': + return {"*", OperatorKind::Unary}; + case 'l': + return {" delete", OperatorKind::Other}; + case 'v': + return {"/", OperatorKind::Binary}; + case 'V': + return {"/=", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'e': + switch (*m_read_ptr++) { + case 'o': + return {"^", OperatorKind::Binary}; + case 'O': + return {"^=", OperatorKind::Binary}; + case 'q': + return {"==", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'g': + switch (*m_read_ptr++) { + case 'e': + return {">=", OperatorKind::Binary}; + case 't': + return {">", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'i': + switch (*m_read_ptr++) { + case 'x': + return {"[]", OperatorKind::Other}; + } + --m_read_ptr; + break; + case 'l': + switch (*m_read_ptr++) { + case 'e': + return {"<=", OperatorKind::Binary}; + case 's': + return {"<<", OperatorKind::Binary}; + case 'S': + return {"<<=", OperatorKind::Binary}; + case 't': + return {"<", OperatorKind::Binary}; + // case 'i': return { "?", OperatorKind::Binary }; + } + --m_read_ptr; + break; + case 'm': + switch (*m_read_ptr++) { + case 'i': + return {"-", OperatorKind::Binary}; + case 'I': + return {"-=", OperatorKind::Binary}; + case 'l': + return {"*", OperatorKind::Binary}; + case 'L': + return {"*=", OperatorKind::Binary}; + case 'm': + return {"--", OperatorKind::Postfix}; + } + --m_read_ptr; + break; + case 'n': + switch (*m_read_ptr++) { + case 'a': + return {" new[]", OperatorKind::Other}; + case 'e': + return {"!=", OperatorKind::Binary}; + case 'g': + return {"-", OperatorKind::Unary}; + case 't': + return {"!", OperatorKind::Unary}; + case 'w': + return {" new", OperatorKind::Other}; + } + --m_read_ptr; + break; + case 'o': + switch (*m_read_ptr++) { + case 'o': + return {"||", OperatorKind::Binary}; + case 'r': + return {"|", OperatorKind::Binary}; + case 'R': + return {"|=", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'p': + switch (*m_read_ptr++) { + case 'm': + return {"->*", OperatorKind::Binary}; + case 's': + return {"+", OperatorKind::Unary}; + case 'l': + return {"+", OperatorKind::Binary}; + case 'L': + return {"+=", OperatorKind::Binary}; + case 'p': + return {"++", OperatorKind::Postfix}; + case 't': + return {"->", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'q': + switch (*m_read_ptr++) { + case 'u': + return {"?", OperatorKind::Ternary}; + } + --m_read_ptr; + break; + case 'r': + switch (*m_read_ptr++) { + case 'm': + return {"%", OperatorKind::Binary}; + case 'M': + return {"%=", OperatorKind::Binary}; + case 's': + return {">>", OperatorKind::Binary}; + case 'S': + return {">=", OperatorKind::Binary}; + } + --m_read_ptr; + break; + case 'v': + char digit = *m_read_ptr; + if (digit >= '0' && digit <= '9') { + m_read_ptr++; + return {nullptr, OperatorKind::Vendor}; + } + --m_read_ptr; + break; + } + --m_read_ptr; + return {nullptr, OperatorKind::NoMatch}; + } + + // <CV-qualifiers> ::= [r] [V] [K] + // <ref-qualifier> ::= R # & ref-qualifier + // <ref-qualifier> ::= O # && ref-qualifier + + int TryParseQualifiers(bool allow_cv, bool allow_ro) { + int qualifiers = QualifierNone; + char next = *m_read_ptr; + if (allow_cv) { + if (next == 'r') // restrict + { + qualifiers |= QualifierRestrict; + next = *++m_read_ptr; + } + if (next == 'V') // volatile + { + qualifiers |= QualifierVolatile; + next = *++m_read_ptr; + } + if (next == 'K') // const + { + qualifiers |= QualifierConst; + next = *++m_read_ptr; + } + } + if (allow_ro) { + if (next == 'R') { + ++m_read_ptr; + qualifiers |= QualifierReference; + } else if (next == 'O') { + ++m_read_ptr; + qualifiers |= QualifierRValueReference; + } + } + return qualifiers; + } + + // <discriminator> := _ <non-negative number> # when number < 10 + // := __ <non-negative number> _ # when number >= 10 + // extension := decimal-digit+ + + int TryParseDiscriminator() { + const char *discriminator_start = m_read_ptr; + + // Test the extension first, since it's what Clang uses + int discriminator_value = TryParseNumber(); + if (discriminator_value != -1) + return discriminator_value; + + char next = *m_read_ptr; + if (next == '_') { + next = *++m_read_ptr; + if (next == '_') { + ++m_read_ptr; + discriminator_value = TryParseNumber(); + if (discriminator_value != -1 && *m_read_ptr++ != '_') { + return discriminator_value; + } + } else if (next >= '0' && next <= '9') { + ++m_read_ptr; + return next - '0'; + } + } + + // Not a valid discriminator + m_read_ptr = discriminator_start; + return -1; + } + + //---------------------------------------------------- + // Parse methods + // + // Consume input starting from m_read_ptr and produce + // buffered output at m_write_ptr + // + // Failures return false and may leave m_read_ptr in an + // indeterminate state + //---------------------------------------------------- + + bool Parse(char character) { + if (*m_read_ptr++ == character) + return true; +#ifdef DEBUG_FAILURES + printf("*** Expected '%c'\n", character); +#endif + return false; + } + + // <number> ::= [n] <non-negative decimal integer> + + bool ParseNumber(bool allow_negative = false) { + if (allow_negative && *m_read_ptr == 'n') { + Write('-'); + ++m_read_ptr; + } + const char *before_digits = m_read_ptr; + while (true) { + unsigned char digit = *m_read_ptr - '0'; + if (digit > 9) + break; + ++m_read_ptr; + } + if (int digit_count = (int)(m_read_ptr - before_digits)) { + Write(before_digits, digit_count); + return true; + } +#ifdef DEBUG_FAILURES + printf("*** Expected number\n"); +#endif + return false; + } + + // <substitution> ::= S <seq-id> _ + // ::= S_ + // <substitution> ::= Sa # ::std::allocator + // <substitution> ::= Sb # ::std::basic_string + // <substitution> ::= Ss # ::std::basic_string < char, + // ::std::char_traits<char>, + // ::std::allocator<char> > + // <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> + // > + // <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> + // > + // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> + // > + + bool ParseSubstitution() { + const char *substitution; + switch (*m_read_ptr) { + case 'a': + substitution = "std::allocator"; + break; + case 'b': + substitution = "std::basic_string"; + break; + case 's': + substitution = "std::string"; + break; + case 'i': + substitution = "std::istream"; + break; + case 'o': + substitution = "std::ostream"; + break; + case 'd': + substitution = "std::iostream"; + break; + default: + // A failed attempt to parse a number will return -1 which turns out to be + // perfect here as S_ is the first substitution, S0_ the next and so forth + int substitution_index = TryParseBase36Number(); + if (*m_read_ptr++ != '_') { +#ifdef DEBUG_FAILURES + printf("*** Expected terminal _ in substitution\n"); +#endif + return false; + } + return RewriteSubstitution(substitution_index + 1); + } + Write(substitution); + ++m_read_ptr; + return true; + } + + // <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E + // + // <bare-function-type> ::= <signature type>+ # types are possible return + // type, then parameter types + + bool ParseFunctionType(int inner_qualifiers = QualifierNone) { +#ifdef DEBUG_FAILURES + printf("*** Function types not supported\n"); +#endif + // TODO: first steps toward an implementation follow, but they're far + // from complete. Function types tend to bracket other types eg: + // int (*)() when used as the type for "name" becomes int (*name)(). + // This makes substitution et al ... interesting. + return false; + +#if 0 // TODO + if (*m_read_ptr == 'Y') + ++m_read_ptr; + + int return_type_start_cookie = GetStartCookie(); + if (!ParseType()) + return false; + Write(' '); + + int insert_cookie = GetStartCookie(); + Write('('); + bool first_param = true; + int qualifiers = QualifierNone; + while (true) + { + switch (*m_read_ptr) + { + case 'E': + ++m_read_ptr; + Write(')'); + break; + case 'v': + ++m_read_ptr; + continue; + case 'R': + case 'O': + if (*(m_read_ptr + 1) == 'E') + { + qualifiers = TryParseQualifiers (false, true); + Parse('E'); + break; + } + // fallthrough + default: + { + if (first_param) + first_param = false; + else WriteCommaSpace(); + + if (!ParseType()) + return false; + continue; + } + } + break; + } + + if (qualifiers) + { + WriteQualifiers (qualifiers); + EndSubstitution (return_type_start_cookie); + } + + if (inner_qualifiers) + { + int qualifier_start_cookie = GetStartCookie(); + Write ('('); + WriteQualifiers (inner_qualifiers); + Write (')'); + ReorderRange (EndRange (qualifier_start_cookie), insert_cookie); + } + return true; +#endif // TODO + } + + // <array-type> ::= A <positive dimension number> _ <element type> + // ::= A [<dimension expression>] _ <element type> + + bool ParseArrayType(int qualifiers = QualifierNone) { +#ifdef DEBUG_FAILURES + printf("*** Array type unsupported\n"); +#endif + // TODO: We fail horribly when recalling these as substitutions or + // templates and trying to constify them eg: + // _ZN4llvm2cl5applyIA28_cNS0_3optIbLb0ENS0_6parserIbEEEEEEvRKT_PT0_ + // + // TODO: Chances are we don't do any better with references and pointers + // that should be type (&) [] instead of type & [] + + return false; + +#if 0 // TODO + if (*m_read_ptr == '_') + { + ++m_read_ptr; + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); + WRITE(" []"); + return true; + } + else + { + const char *before_digits = m_read_ptr; + if (TryParseNumber() != -1) + { + const char *after_digits = m_read_ptr; + if (!Parse('_')) + return false; + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); + Write(' '); + Write('['); + Write(before_digits, after_digits - before_digits); + } + else + { + int type_insertion_cookie = GetStartCookie(); + if (!ParseExpression()) + return false; + if (!Parse('_')) + return false; + + int type_start_cookie = GetStartCookie(); + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); + Write(' '); + Write('['); + ReorderRange (EndRange (type_start_cookie), type_insertion_cookie); + } + Write(']'); + return true; + } +#endif // TODO + } + + // <pointer-to-member-type> ::= M <class type> <member type> + + // TODO: Determine how to handle pointers to function members correctly, + // currently not an issue because we don't have function types at all... + bool ParsePointerToMemberType() { + int insertion_cookie = GetStartCookie(); + Write(' '); + if (!ParseType()) + return false; + WRITE("::*"); + + int type_cookie = GetStartCookie(); + if (!ParseType()) + return false; + ReorderRange(EndRange(type_cookie), insertion_cookie); + return true; + } + + // <template-param> ::= T_ # first template parameter + // ::= T <parameter-2 non-negative number> _ + + bool ParseTemplateParam() { + int count = TryParseNumber(); + if (!Parse('_')) + return false; + + // When no number is present we get -1, which is convenient since + // T_ is the zeroth element T0_ is element 1, and so on + return RewriteTemplateArg(count + 1); + } + + // <vector-type> + // Dv <dimension number> _ <vector type> + bool TryParseVectorType() { + const int dimension = TryParseNumber(); + if (dimension == -1) + return false; + + if (*m_read_ptr++ != '_') + return false; + + char vec_dimens[32] = {'\0'}; + ::snprintf(vec_dimens, sizeof vec_dimens - 1, " __vector(%d)", dimension); + ParseType(); + Write(vec_dimens); + return true; + } + + // <type> ::= <builtin-type> + // ::= <function-type> + // ::= <class-enum-type> + // ::= <array-type> + // ::= <pointer-to-member-type> + // ::= <template-param> + // ::= <template-template-param> <template-args> + // ::= <decltype> + // ::= <substitution> + // ::= <CV-qualifiers> <type> + // ::= P <type> # pointer-to + // ::= R <type> # reference-to + // ::= O <type> # rvalue reference-to (C++0x) + // ::= C <type> # complex pair (C 2000) + // ::= G <type> # imaginary (C 2000) + // ::= Dp <type> # pack expansion (C++0x) + // ::= U <source-name> <type> # vendor extended type qualifier + // extension := U <objc-name> <objc-type> # objc-type<identifier> + // extension := <vector-type> # <vector-type> starts with Dv + + // <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + + // <number of digits in k1> + k1 + // <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> + // 11objc_object -> id<source-name> + + bool ParseType() { +#ifdef DEBUG_FAILURES + const char *failed_type = m_read_ptr; +#endif + int type_start_cookie = GetStartCookie(); + bool suppress_substitution = false; + + int qualifiers = TryParseQualifiers(true, false); + switch (*m_read_ptr) { + case 'D': + ++m_read_ptr; + switch (*m_read_ptr++) { + case 'p': + if (!ParseType()) + return false; + break; + case 'v': + if (!TryParseVectorType()) + return false; + break; + case 'T': + case 't': + default: +#ifdef DEBUG_FAILURES + printf("*** Unsupported type: %.3s\n", failed_type); +#endif + return false; + } + break; + case 'T': + ++m_read_ptr; + if (!ParseTemplateParam()) + return false; + break; + case 'M': + ++m_read_ptr; + if (!ParsePointerToMemberType()) + return false; + break; + case 'A': + ++m_read_ptr; + if (!ParseArrayType()) + return false; + break; + case 'F': + ++m_read_ptr; + if (!ParseFunctionType()) + return false; + break; + case 'S': + if (*++m_read_ptr == 't') { + ++m_read_ptr; + WriteStdPrefix(); + if (!ParseName()) + return false; + } else { + suppress_substitution = true; + if (!ParseSubstitution()) + return false; + } + break; + case 'P': { + switch (*++m_read_ptr) { + case 'F': + ++m_read_ptr; + if (!ParseFunctionType(QualifierPointer)) + return false; + break; + default: + if (!ParseType()) + return false; + Write('*'); + break; + } + break; + } + case 'R': { + ++m_read_ptr; + if (!ParseType()) + return false; + Write('&'); + break; + } + case 'O': { + ++m_read_ptr; + if (!ParseType()) + return false; + Write('&'); + Write('&'); + break; + } + case 'C': + case 'G': + case 'U': +#ifdef DEBUG_FAILURES + printf("*** Unsupported type: %.3s\n", failed_type); +#endif + return false; + // Test for common cases to avoid TryParseBuiltinType() overhead + case 'N': + case 'Z': + case 'L': + if (!ParseName()) + return false; + break; + default: + if (const char *builtin = TryParseBuiltinType()) { + Write(builtin); + suppress_substitution = true; + } else { + if (!ParseName()) + return false; + } + break; + } + + // Allow base substitutions to be suppressed, but always record + // substitutions for the qualified variant + if (!suppress_substitution) + EndSubstitution(type_start_cookie); + if (qualifiers) { + WriteQualifiers(qualifiers, false); + EndSubstitution(type_start_cookie); + } + return true; + } + + // <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ + // ::= <closure-type-name> + // + // <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ + // + // <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda + // has no parameters + + bool ParseUnnamedTypeName(NameState &name_state) { + switch (*m_read_ptr++) { + case 't': { + int cookie = GetStartCookie(); + WRITE("'unnamed"); + const char *before_digits = m_read_ptr; + if (TryParseNumber() != -1) + Write(before_digits, m_read_ptr - before_digits); + if (!Parse('_')) + return false; + Write('\''); + name_state.last_name_range = EndRange(cookie); + return true; + } + case 'b': { + int cookie = GetStartCookie(); + WRITE("'block"); + const char *before_digits = m_read_ptr; + if (TryParseNumber() != -1) + Write(before_digits, m_read_ptr - before_digits); + if (!Parse('_')) + return false; + Write('\''); + name_state.last_name_range = EndRange(cookie); + return true; + } + case 'l': +#ifdef DEBUG_FAILURES + printf("*** Lambda type names unsupported\n"); +#endif + return false; + } +#ifdef DEBUG_FAILURES + printf("*** Unknown unnamed type %.3s\n", m_read_ptr - 2); +#endif + return false; + } + + // <ctor-dtor-name> ::= C1 # complete object constructor + // ::= C2 # base object constructor + // ::= C3 # complete object allocating constructor + + bool ParseCtor(NameState &name_state) { + char next = *m_read_ptr; + if (next == '1' || next == '2' || next == '3' || next == '5') { + RewriteRange(name_state.last_name_range); + name_state.has_no_return_type = true; + ++m_read_ptr; + return true; + } +#ifdef DEBUG_FAILURES + printf("*** Broken constructor\n"); +#endif + return false; + } + + // <ctor-dtor-name> ::= D0 # deleting destructor + // ::= D1 # complete object destructor + // ::= D2 # base object destructor + + bool ParseDtor(NameState &name_state) { + char next = *m_read_ptr; + if (next == '0' || next == '1' || next == '2' || next == '5') { + Write('~'); + RewriteRange(name_state.last_name_range); + name_state.has_no_return_type = true; + ++m_read_ptr; + return true; + } +#ifdef DEBUG_FAILURES + printf("*** Broken destructor\n"); +#endif + return false; + } + + // See TryParseOperator() + + bool ParseOperatorName(NameState &name_state) { +#ifdef DEBUG_FAILURES + const char *operator_ptr = m_read_ptr; +#endif + Operator parsed_operator = TryParseOperator(); + if (parsed_operator.name) { + WRITE("operator"); + Write(parsed_operator.name); + return true; + } + + // Handle special operators + switch (parsed_operator.kind) { + case OperatorKind::Vendor: + WRITE("operator "); + return ParseSourceName(); + case OperatorKind::ConversionOperator: + ResetTemplateArgs(); + name_state.has_no_return_type = true; + WRITE("operator "); + return ParseType(); + default: +#ifdef DEBUG_FAILURES + printf("*** Unknown operator: %.2s\n", operator_ptr); +#endif + return false; + } + } + + // <source-name> ::= <positive length number> <identifier> + + bool ParseSourceName() { + int count = TryParseNumber(); + if (count == -1) { +#ifdef DEBUG_FAILURES + printf("*** Malformed source name, missing length count\n"); +#endif + return false; + } + + const char *next_m_read_ptr = m_read_ptr + count; + if (next_m_read_ptr > m_read_end) { +#ifdef DEBUG_FAILURES + printf("*** Malformed source name, premature termination\n"); +#endif + return false; + } + + if (count >= 10 && strncmp(m_read_ptr, "_GLOBAL__N", 10) == 0) + WRITE("(anonymous namespace)"); + else + Write(m_read_ptr, count); + + m_read_ptr = next_m_read_ptr; + return true; + } + + // <unqualified-name> ::= <operator-name> + // ::= <ctor-dtor-name> + // ::= <source-name> + // ::= <unnamed-type-name> + + bool ParseUnqualifiedName(NameState &name_state) { + // Note that these are detected directly in ParseNestedName for + // performance rather than switching on the same options twice + char next = *m_read_ptr; + switch (next) { + case 'C': + ++m_read_ptr; + return ParseCtor(name_state); + case 'D': + ++m_read_ptr; + return ParseDtor(name_state); + case 'U': + ++m_read_ptr; + return ParseUnnamedTypeName(name_state); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + int name_start_cookie = GetStartCookie(); + if (!ParseSourceName()) + return false; + name_state.last_name_range = EndRange(name_start_cookie); + return true; + } + default: + return ParseOperatorName(name_state); + }; + } + + // <unscoped-name> ::= <unqualified-name> + // ::= St <unqualified-name> # ::std:: + // extension ::= StL<unqualified-name> + + bool ParseUnscopedName(NameState &name_state) { + if (*m_read_ptr == 'S' && *(m_read_ptr + 1) == 't') { + WriteStdPrefix(); + if (*(m_read_ptr += 2) == 'L') + ++m_read_ptr; + } + return ParseUnqualifiedName(name_state); + } + + bool ParseIntegerLiteral(const char *prefix, const char *suffix, + bool allow_negative) { + if (prefix) + Write(prefix); + if (!ParseNumber(allow_negative)) + return false; + if (suffix) + Write(suffix); + return Parse('E'); + } + + bool ParseBooleanLiteral() { + switch (*m_read_ptr++) { + case '0': + WRITE("false"); + break; + case '1': + WRITE("true"); + break; + default: +#ifdef DEBUG_FAILURES + printf("*** Boolean literal not 0 or 1\n"); +#endif + return false; + } + return Parse('E'); + } + + // <expr-primary> ::= L <type> <value number> E # + // integer literal + // ::= L <type> <value float> E # + // floating literal + // ::= L <string type> E # + // string literal + // ::= L <nullptr type> E # + // nullptr literal (i.e., "LDnE") + // ::= L <type> <real-part float> _ <imag-part float> E # + // complex floating point literal (C 2000) + // ::= L <mangled-name> E # + // external name + + bool ParseExpressionPrimary() { + switch (*m_read_ptr++) { + case 'b': + return ParseBooleanLiteral(); + case 'x': + return ParseIntegerLiteral(nullptr, "ll", true); + case 'l': + return ParseIntegerLiteral(nullptr, "l", true); + case 'i': + return ParseIntegerLiteral(nullptr, nullptr, true); + case 'n': + return ParseIntegerLiteral("(__int128)", nullptr, true); + case 'j': + return ParseIntegerLiteral(nullptr, "u", false); + case 'm': + return ParseIntegerLiteral(nullptr, "ul", false); + case 'y': + return ParseIntegerLiteral(nullptr, "ull", false); + case 'o': + return ParseIntegerLiteral("(unsigned __int128)", nullptr, false); + case '_': + if (*m_read_ptr++ == 'Z') { + if (!ParseEncoding()) + return false; + return Parse('E'); + } + --m_read_ptr; + LLVM_FALLTHROUGH; + case 'w': + case 'c': + case 'a': + case 'h': + case 's': + case 't': + case 'f': + case 'd': + case 'e': +#ifdef DEBUG_FAILURES + printf("*** Unsupported primary expression %.5s\n", m_read_ptr - 1); +#endif + return false; + case 'T': +// Invalid mangled name per +// http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html +#ifdef DEBUG_FAILURES + printf("*** Invalid primary expr encoding\n"); +#endif + return false; + default: + --m_read_ptr; + Write('('); + if (!ParseType()) + return false; + Write(')'); + if (!ParseNumber()) + return false; + return Parse('E'); + } + } + + // <unresolved-type> ::= <template-param> + // ::= <decltype> + // ::= <substitution> + + bool ParseUnresolvedType() { + int type_start_cookie = GetStartCookie(); + switch (*m_read_ptr++) { + case 'T': + if (!ParseTemplateParam()) + return false; + EndSubstitution(type_start_cookie); + return true; + case 'S': { + if (*m_read_ptr != 't') + return ParseSubstitution(); + + ++m_read_ptr; + WriteStdPrefix(); + NameState type_name = {}; + if (!ParseUnqualifiedName(type_name)) + return false; + EndSubstitution(type_start_cookie); + return true; + } + case 'D': + default: +#ifdef DEBUG_FAILURES + printf("*** Unsupported unqualified type: %3s\n", m_read_ptr - 1); +#endif + return false; + } + } + + // <base-unresolved-name> ::= <simple-id> # + // unresolved name + // extension ::= <operator-name> # + // unresolved operator-function-id + // extension ::= <operator-name> <template-args> # + // unresolved operator template-id + // ::= on <operator-name> # + // unresolved operator-function-id + // ::= on <operator-name> <template-args> # + // unresolved operator template-id + // ::= dn <destructor-name> # + // destructor or pseudo-destructor; + // # + // e.g. + // ~X + // or + // ~X<N-1> + + bool ParseBaseUnresolvedName() { +#ifdef DEBUG_FAILURES + printf("*** Base unresolved name unsupported\n"); +#endif + return false; + } + + // <unresolved-name> + // extension ::= srN <unresolved-type> [<template-args>] + // <unresolved-qualifier-level>* E <base-unresolved-name> + // ::= [gs] <base-unresolved-name> # x + // or (with "gs") ::x + // ::= [gs] sr <unresolved-qualifier-level>+ E + // <base-unresolved-name> + // # + // A::x, + // N::y, + // A<T>::z; + // "gs" + // means + // leading + // "::" + // ::= sr <unresolved-type> <base-unresolved-name> # + // T::x / decltype(p)::x + // extension ::= sr <unresolved-type> <template-args> + // <base-unresolved-name> + // # + // T::N::x + // /decltype(p)::N::x + // (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ + // E <base-unresolved-name> + + bool ParseUnresolvedName() { +#ifdef DEBUG_FAILURES + printf("*** Unresolved names not supported\n"); +#endif + // TODO: grammar for all of this seems unclear... + return false; + +#if 0 // TODO + if (*m_read_ptr == 'g' && *(m_read_ptr + 1) == 's') + { + m_read_ptr += 2; + WriteNamespaceSeparator(); + } +#endif // TODO + } + + // <expression> ::= <unary operator-name> <expression> + // ::= <binary operator-name> <expression> <expression> + // ::= <ternary operator-name> <expression> <expression> + // <expression> + // ::= cl <expression>+ E # + // call + // ::= cv <type> <expression> # + // conversion with one argument + // ::= cv <type> _ <expression>* E # + // conversion with a different number of arguments + // ::= [gs] nw <expression>* _ <type> E # new + // (expr-list) type + // ::= [gs] nw <expression>* _ <type> <initializer> # new + // (expr-list) type (init) + // ::= [gs] na <expression>* _ <type> E # + // new[] (expr-list) type + // ::= [gs] na <expression>* _ <type> <initializer> # + // new[] (expr-list) type (init) + // ::= [gs] dl <expression> # + // delete expression + // ::= [gs] da <expression> # + // delete[] expression + // ::= pp_ <expression> # + // prefix ++ + // ::= mm_ <expression> # + // prefix -- + // ::= ti <type> # + // typeid (type) + // ::= te <expression> # + // typeid (expression) + // ::= dc <type> <expression> # + // dynamic_cast<type> (expression) + // ::= sc <type> <expression> # + // static_cast<type> (expression) + // ::= cc <type> <expression> # + // const_cast<type> (expression) + // ::= rc <type> <expression> # + // reinterpret_cast<type> (expression) + // ::= st <type> # + // sizeof (a type) + // ::= sz <expression> # + // sizeof (an expression) + // ::= at <type> # + // alignof (a type) + // ::= az <expression> # + // alignof (an expression) + // ::= nx <expression> # + // noexcept (expression) + // ::= <template-param> + // ::= <function-param> + // ::= dt <expression> <unresolved-name> # + // expr.name + // ::= pt <expression> <unresolved-name> # + // expr->name + // ::= ds <expression> <expression> # + // expr.*expr + // ::= sZ <template-param> # + // size of a parameter pack + // ::= sZ <function-param> # + // size of a function parameter pack + // ::= sp <expression> # + // pack expansion + // ::= tw <expression> # + // throw expression + // ::= tr # + // throw with no operand (rethrow) + // ::= <unresolved-name> # + // f(p), N::f(p), ::f(p), + // # + // freestanding + // dependent + // name + // (e.g., + // T::x), + // # + // objectless + // nonstatic + // member + // reference + // ::= <expr-primary> + + bool ParseExpression() { + Operator expression_operator = TryParseOperator(); + switch (expression_operator.kind) { + case OperatorKind::Unary: + Write(expression_operator.name); + Write('('); + if (!ParseExpression()) + return false; + Write(')'); + return true; + case OperatorKind::Binary: + if (!ParseExpression()) + return false; + Write(expression_operator.name); + return ParseExpression(); + case OperatorKind::Ternary: + if (!ParseExpression()) + return false; + Write('?'); + if (!ParseExpression()) + return false; + Write(':'); + return ParseExpression(); + case OperatorKind::NoMatch: + break; + case OperatorKind::Other: + default: +#ifdef DEBUG_FAILURES + printf("*** Unsupported operator: %s\n", expression_operator.name); +#endif + return false; + } + + switch (*m_read_ptr++) { + case 'T': + return ParseTemplateParam(); + case 'L': + return ParseExpressionPrimary(); + case 's': + if (*m_read_ptr++ == 'r') + return ParseUnresolvedName(); + --m_read_ptr; + LLVM_FALLTHROUGH; + default: + return ParseExpressionPrimary(); + } + } + + // <template-arg> ::= <type> # + // type or template + // ::= X <expression> E # + // expression + // ::= <expr-primary> # + // simple expressions + // ::= J <template-arg>* E # + // argument pack + // ::= LZ <encoding> E # + // extension + + bool ParseTemplateArg() { + switch (*m_read_ptr) { + case 'J': +#ifdef DEBUG_FAILURES + printf("*** Template argument packs unsupported\n"); +#endif + return false; + case 'X': + ++m_read_ptr; + if (!ParseExpression()) + return false; + return Parse('E'); + case 'L': + ++m_read_ptr; + return ParseExpressionPrimary(); + default: + return ParseType(); + } + } + + // <template-args> ::= I <template-arg>* E + // extension, the abi says <template-arg>+ + + bool ParseTemplateArgs(bool record_template_args = false) { + if (record_template_args) + ResetTemplateArgs(); + + bool first_arg = true; + while (*m_read_ptr != 'E') { + if (first_arg) + first_arg = false; + else + WriteCommaSpace(); + + int template_start_cookie = GetStartCookie(); + if (!ParseTemplateArg()) + return false; + if (record_template_args) + EndTemplateArg(template_start_cookie); + } + ++m_read_ptr; + return true; + } + + // <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> + // <unqualified-name> E + // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> + // <template-args> E + // + // <prefix> ::= <prefix> <unqualified-name> + // ::= <template-prefix> <template-args> + // ::= <template-param> + // ::= <decltype> + // ::= # empty + // ::= <substitution> + // ::= <prefix> <data-member-prefix> + // extension ::= L + // + // <template-prefix> ::= <prefix> <template unqualified-name> + // ::= <template-param> + // ::= <substitution> + // + // <unqualified-name> ::= <operator-name> + // ::= <ctor-dtor-name> + // ::= <source-name> + // ::= <unnamed-type-name> + + bool ParseNestedName(NameState &name_state, + bool parse_discriminator = false) { + int qualifiers = TryParseQualifiers(true, true); + bool first_part = true; + bool suppress_substitution = true; + int name_start_cookie = GetStartCookie(); + while (true) { + char next = *m_read_ptr; + if (next == 'E') { + ++m_read_ptr; + break; + } + + // Record a substitution candidate for all prefixes, but not the full name + if (suppress_substitution) + suppress_substitution = false; + else + EndSubstitution(name_start_cookie); + + if (next == 'I') { + ++m_read_ptr; + name_state.is_last_generic = true; + WriteTemplateStart(); + if (!ParseTemplateArgs(name_state.parse_function_params)) + return false; + WriteTemplateEnd(); + continue; + } + + if (first_part) + first_part = false; + else + WriteNamespaceSeparator(); + + name_state.is_last_generic = false; + switch (next) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + int name_start_cookie = GetStartCookie(); + if (!ParseSourceName()) + return false; + name_state.last_name_range = EndRange(name_start_cookie); + continue; + } + case 'S': + if (*++m_read_ptr == 't') { + WriteStdPrefix(); + ++m_read_ptr; + if (!ParseUnqualifiedName(name_state)) + return false; + } else { + if (!ParseSubstitution()) + return false; + suppress_substitution = true; + } + continue; + case 'T': + ++m_read_ptr; + if (!ParseTemplateParam()) + return false; + continue; + case 'C': + ++m_read_ptr; + if (!ParseCtor(name_state)) + return false; + continue; + case 'D': { + switch (*(m_read_ptr + 1)) { + case 't': + case 'T': +#ifdef DEBUG_FAILURES + printf("*** Decltype unsupported\n"); +#endif + return false; + } + ++m_read_ptr; + if (!ParseDtor(name_state)) + return false; + continue; + } + case 'U': + ++m_read_ptr; + if (!ParseUnnamedTypeName(name_state)) + return false; + continue; + case 'L': + ++m_read_ptr; + if (!ParseUnqualifiedName(name_state)) + return false; + continue; + default: + if (!ParseOperatorName(name_state)) + return false; + } + } + + if (parse_discriminator) + TryParseDiscriminator(); + if (name_state.parse_function_params && + !ParseFunctionArgs(name_state, name_start_cookie)) { + return false; + } + if (qualifiers) + WriteQualifiers(qualifiers); + return true; + } + + // <local-name> := Z <function encoding> E <entity name> [<discriminator>] + // := Z <function encoding> E s [<discriminator>] + // := Z <function encoding> Ed [ <parameter number> ] _ <entity + // name> + + bool ParseLocalName(bool parse_function_params) { + if (!ParseEncoding()) + return false; + if (!Parse('E')) + return false; + + switch (*m_read_ptr) { + case 's': + ++m_read_ptr; + TryParseDiscriminator(); // Optional and ignored + WRITE("::string literal"); + break; + case 'd': + ++m_read_ptr; + TryParseNumber(); // Optional and ignored + if (!Parse('_')) + return false; + WriteNamespaceSeparator(); + if (!ParseName()) + return false; + break; + default: + WriteNamespaceSeparator(); + if (!ParseName(parse_function_params, true)) + return false; + TryParseDiscriminator(); // Optional and ignored + } + return true; + } + + // <name> ::= <nested-name> + // ::= <local-name> + // ::= <unscoped-template-name> <template-args> + // ::= <unscoped-name> + + // <unscoped-template-name> ::= <unscoped-name> + // ::= <substitution> + + bool ParseName(bool parse_function_params = false, + bool parse_discriminator = false) { + NameState name_state = {parse_function_params, false, false, {0, 0}}; + int name_start_cookie = GetStartCookie(); + + switch (*m_read_ptr) { + case 'N': + ++m_read_ptr; + return ParseNestedName(name_state, parse_discriminator); + case 'Z': { + ++m_read_ptr; + if (!ParseLocalName(parse_function_params)) + return false; + break; + } + case 'L': + ++m_read_ptr; + LLVM_FALLTHROUGH; + default: { + if (!ParseUnscopedName(name_state)) + return false; + + if (*m_read_ptr == 'I') { + EndSubstitution(name_start_cookie); + + ++m_read_ptr; + name_state.is_last_generic = true; + WriteTemplateStart(); + if (!ParseTemplateArgs(parse_function_params)) + return false; + WriteTemplateEnd(); + } + break; + } + } + if (parse_discriminator) + TryParseDiscriminator(); + if (parse_function_params && + !ParseFunctionArgs(name_state, name_start_cookie)) { + return false; + } + return true; + } + + // <call-offset> ::= h <nv-offset> _ + // ::= v <v-offset> _ + // + // <nv-offset> ::= <offset number> + // # non-virtual base override + // + // <v-offset> ::= <offset number> _ <virtual offset number> + // # virtual base override, with vcall offset + + bool ParseCallOffset() { + switch (*m_read_ptr++) { + case 'h': + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; + return true; + case 'v': + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; + return true; + } +#ifdef DEBUG_FAILURES + printf("*** Malformed call offset\n"); +#endif + return false; + } + + // <special-name> ::= TV <type> # virtual table + // ::= TT <type> # VTT structure (construction vtable index) + // ::= TI <type> # typeinfo structure + // ::= TS <type> # typeinfo name (null-terminated byte + // string) + // ::= Tc <call-offset> <call-offset> <base encoding> + // # base is the nominal target function of thunk + // # first call-offset is 'this' adjustment + // # second call-offset is result adjustment + // ::= T <call-offset> <base encoding> + // # base is the nominal target function of thunk + // extension ::= TC <first type> <number> _ <second type> # construction + // vtable for second-in-first + + bool ParseSpecialNameT() { + switch (*m_read_ptr++) { + case 'V': + WRITE("vtable for "); + return ParseType(); + case 'T': + WRITE("VTT for "); + return ParseType(); + case 'I': + WRITE("typeinfo for "); + return ParseType(); + case 'S': + WRITE("typeinfo name for "); + return ParseType(); + case 'c': + case 'C': +#ifdef DEBUG_FAILURES + printf("*** Unsupported thunk or construction vtable name: %.3s\n", + m_read_ptr - 1); +#endif + return false; + default: + if (*--m_read_ptr == 'v') { + WRITE("virtual thunk to "); + } else { + WRITE("non-virtual thunk to "); + } + if (!ParseCallOffset()) + return false; + return ParseEncoding(); + } + } + + // <special-name> ::= GV <object name> # Guard variable for one-time + // initialization + // # No <type> + // extension ::= GR <object name> # reference temporary for object + + bool ParseSpecialNameG() { + switch (*m_read_ptr++) { + case 'V': + WRITE("guard variable for "); + if (!ParseName(true)) + return false; + break; + case 'R': + WRITE("reference temporary for "); + if (!ParseName(true)) + return false; + break; + default: +#ifdef DEBUG_FAILURES + printf("*** Unknown G encoding\n"); +#endif + return false; + } + return true; + } + + // <bare-function-type> ::= <signature type>+ # types are possible + // return type, then parameter types + + bool ParseFunctionArgs(NameState &name_state, int return_insert_cookie) { + char next = *m_read_ptr; + if (next == 'E' || next == '\0' || next == '.') + return true; + + // Clang has a bad habit of making unique manglings by just sticking numbers + // on the end of a symbol, + // which is ambiguous with malformed source name manglings + const char *before_clang_uniquing_test = m_read_ptr; + if (TryParseNumber()) { + if (*m_read_ptr == '\0') + return true; + m_read_ptr = before_clang_uniquing_test; + } + + if (name_state.is_last_generic && !name_state.has_no_return_type) { + int return_type_start_cookie = GetStartCookie(); + if (!ParseType()) + return false; + Write(' '); + ReorderRange(EndRange(return_type_start_cookie), return_insert_cookie); + } + + Write('('); + bool first_param = true; + while (true) { + switch (*m_read_ptr) { + case '\0': + case 'E': + case '.': + break; + case 'v': + ++m_read_ptr; + continue; + case '_': + // Not a formal part of the mangling specification, but clang emits + // suffixes starting with _block_invoke + if (strncmp(m_read_ptr, "_block_invoke", 13) == 0) { + m_read_ptr += strlen(m_read_ptr); + break; + } + LLVM_FALLTHROUGH; + default: + if (first_param) + first_param = false; + else + WriteCommaSpace(); + + if (!ParseType()) + return false; + continue; + } + break; + } + Write(')'); + return true; + } + + // <encoding> ::= <function name> <bare-function-type> + // ::= <data name> + // ::= <special-name> + + bool ParseEncoding() { + switch (*m_read_ptr) { + case 'T': + ++m_read_ptr; + if (!ParseSpecialNameT()) + return false; + break; + case 'G': + ++m_read_ptr; + if (!ParseSpecialNameG()) + return false; + break; + default: + if (!ParseName(true)) + return false; + break; + } + return true; + } + + bool ParseMangling(const char *mangled_name, long mangled_name_length = 0) { + if (!mangled_name_length) + mangled_name_length = strlen(mangled_name); + m_read_end = mangled_name + mangled_name_length; + m_read_ptr = mangled_name; + m_write_ptr = m_buffer; + m_next_substitute_index = 0; + m_next_template_arg_index = m_rewrite_ranges_size - 1; + + if (*m_read_ptr++ != '_' || *m_read_ptr++ != 'Z') { +#ifdef DEBUG_FAILURES + printf("*** Missing _Z prefix\n"); +#endif + return false; + } + if (!ParseEncoding()) + return false; + switch (*m_read_ptr) { + case '.': + Write(' '); + Write('('); + Write(m_read_ptr, m_read_end - m_read_ptr); + Write(')'); + LLVM_FALLTHROUGH; + case '\0': + return true; + default: +#ifdef DEBUG_FAILURES + printf("*** Unparsed mangled content\n"); +#endif + return false; + } + } + +private: + // External scratch storage used during demanglings + + char *m_buffer; + const char *m_buffer_end; + BufferRange *m_rewrite_ranges; + int m_rewrite_ranges_size; + bool m_owns_buffer; + bool m_owns_m_rewrite_ranges; + + // Internal state used during demangling + + const char *m_read_ptr; + const char *m_read_end; + char *m_write_ptr; + int m_next_template_arg_index; + int m_next_substitute_index; + std::function<void(const char *s)> m_builtins_hook; +}; + +} // Anonymous namespace + +// Public entry points referenced from Mangled.cpp +namespace lldb_private { +char *FastDemangle(const char *mangled_name) { + char buffer[16384]; + SymbolDemangler demangler(buffer, sizeof(buffer)); + return demangler.GetDemangledCopy(mangled_name); +} + +char *FastDemangle(const char *mangled_name, size_t mangled_name_length, + std::function<void(const char *s)> builtins_hook) { + char buffer[16384]; + SymbolDemangler demangler(buffer, sizeof(buffer), builtins_hook); + return demangler.GetDemangledCopy(mangled_name, mangled_name_length); +} +} // lldb_private namespace diff --git a/contrib/llvm/tools/lldb/source/Utility/FileSpec.cpp b/contrib/llvm/tools/lldb/source/Utility/FileSpec.cpp new file mode 100644 index 0000000..3c4e340 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/FileSpec.cpp @@ -0,0 +1,966 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/TildeExpressionResolver.h" + +#include "llvm/ADT/SmallString.h" // for SmallString +#include "llvm/ADT/SmallVector.h" // for SmallVectorTemplat... +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" // for Triple +#include "llvm/ADT/Twine.h" // for Twine +#include "llvm/Config/llvm-config.h" // for LLVM_ON_WIN32 +#include "llvm/Support/ErrorOr.h" // for ErrorOr +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" // for raw_ostream, fs + +#include <algorithm> // for replace, min, unique +#include <system_error> // for error_code +#include <vector> // for vector + +#include <assert.h> // for assert +#include <stdio.h> // for size_t, NULL, snpr... +#include <string.h> // for strcmp + +using namespace lldb; +using namespace lldb_private; + +namespace { + +static constexpr FileSpec::PathSyntax GetNativeSyntax() { +#if defined(LLVM_ON_WIN32) + return FileSpec::ePathSyntaxWindows; +#else + return FileSpec::ePathSyntaxPosix; +#endif +} + +bool PathSyntaxIsPosix(FileSpec::PathSyntax syntax) { + return (syntax == FileSpec::ePathSyntaxPosix || + (syntax == FileSpec::ePathSyntaxHostNative && + GetNativeSyntax() == FileSpec::ePathSyntaxPosix)); +} + +const char *GetPathSeparators(FileSpec::PathSyntax syntax) { + return PathSyntaxIsPosix(syntax) ? "/" : "\\/"; +} + +char GetPreferredPathSeparator(FileSpec::PathSyntax syntax) { + return GetPathSeparators(syntax)[0]; +} + +bool IsPathSeparator(char value, FileSpec::PathSyntax syntax) { + return value == '/' || (!PathSyntaxIsPosix(syntax) && value == '\\'); +} + +void Normalize(llvm::SmallVectorImpl<char> &path, FileSpec::PathSyntax syntax) { + if (PathSyntaxIsPosix(syntax)) + return; + + std::replace(path.begin(), path.end(), '\\', '/'); + // Windows path can have \\ slashes which can be changed by replace + // call above to //. Here we remove the duplicate. + auto iter = std::unique(path.begin(), path.end(), [](char &c1, char &c2) { + return (c1 == '/' && c2 == '/'); + }); + path.erase(iter, path.end()); +} + +void Denormalize(llvm::SmallVectorImpl<char> &path, + FileSpec::PathSyntax syntax) { + if (PathSyntaxIsPosix(syntax)) + return; + + std::replace(path.begin(), path.end(), '/', '\\'); +} + +size_t FilenamePos(llvm::StringRef str, FileSpec::PathSyntax syntax) { + if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1]) + return 0; + + if (str.size() > 0 && IsPathSeparator(str.back(), syntax)) + return str.size() - 1; + + size_t pos = str.find_last_of(GetPathSeparators(syntax), str.size() - 1); + + if (!PathSyntaxIsPosix(syntax) && pos == llvm::StringRef::npos) + pos = str.find_last_of(':', str.size() - 2); + + if (pos == llvm::StringRef::npos || + (pos == 1 && IsPathSeparator(str[0], syntax))) + return 0; + + return pos + 1; +} + +size_t RootDirStart(llvm::StringRef str, FileSpec::PathSyntax syntax) { + // case "c:/" + if (!PathSyntaxIsPosix(syntax) && + (str.size() > 2 && str[1] == ':' && IsPathSeparator(str[2], syntax))) + return 2; + + // case "//" + if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1]) + return llvm::StringRef::npos; + + // case "//net" + if (str.size() > 3 && IsPathSeparator(str[0], syntax) && str[0] == str[1] && + !IsPathSeparator(str[2], syntax)) + return str.find_first_of(GetPathSeparators(syntax), 2); + + // case "/" + if (str.size() > 0 && IsPathSeparator(str[0], syntax)) + return 0; + + return llvm::StringRef::npos; +} + +size_t ParentPathEnd(llvm::StringRef path, FileSpec::PathSyntax syntax) { + size_t end_pos = FilenamePos(path, syntax); + + bool filename_was_sep = + path.size() > 0 && IsPathSeparator(path[end_pos], syntax); + + // Skip separators except for root dir. + size_t root_dir_pos = RootDirStart(path.substr(0, end_pos), syntax); + + while (end_pos > 0 && (end_pos - 1) != root_dir_pos && + IsPathSeparator(path[end_pos - 1], syntax)) + --end_pos; + + if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep) + return llvm::StringRef::npos; + + return end_pos; +} + +} // end anonymous namespace + +void FileSpec::Resolve(llvm::SmallVectorImpl<char> &path) { + if (path.empty()) + return; + + llvm::SmallString<32> Source(path.begin(), path.end()); + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(Source, path); + + // Save a copy of the original path that's passed in + llvm::SmallString<128> original_path(path.begin(), path.end()); + + llvm::sys::fs::make_absolute(path); + if (!llvm::sys::fs::exists(path)) { + path.clear(); + path.append(original_path.begin(), original_path.end()); + } +} + +FileSpec::FileSpec() : m_syntax(GetNativeSyntax()) {} + +//------------------------------------------------------------------ +// Default constructor that can take an optional full path to a +// file on disk. +//------------------------------------------------------------------ +FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, PathSyntax syntax) + : m_syntax(syntax) { + SetFile(path, resolve_path, syntax); +} + +FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, + const llvm::Triple &Triple) + : FileSpec{path, resolve_path, + Triple.isOSWindows() ? ePathSyntaxWindows : ePathSyntaxPosix} {} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec &rhs) + : m_directory(rhs.m_directory), m_filename(rhs.m_filename), + m_is_resolved(rhs.m_is_resolved), m_syntax(rhs.m_syntax) {} + +//------------------------------------------------------------------ +// 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() {} + +//------------------------------------------------------------------ +// 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_syntax = rhs.m_syntax; + } + return *this; +} + +//------------------------------------------------------------------ +// 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, bool resolve, + PathSyntax syntax) { + // CLEANUP: Use StringRef for string handling. This function is kind of a + // mess and the unclear semantics of RootDirStart and ParentPathEnd make + // it very difficult to understand this function. There's no reason this + // function should be particularly complicated or difficult to understand. + m_filename.Clear(); + m_directory.Clear(); + m_is_resolved = false; + m_syntax = (syntax == ePathSyntaxHostNative) ? GetNativeSyntax() : syntax; + + if (pathname.empty()) + return; + + llvm::SmallString<64> resolved(pathname); + + if (resolve) { + FileSpec::Resolve(resolved); + m_is_resolved = true; + } + + Normalize(resolved, m_syntax); + + llvm::StringRef resolve_path_ref(resolved.c_str()); + size_t dir_end = ParentPathEnd(resolve_path_ref, m_syntax); + if (dir_end == 0) { + m_filename.SetString(resolve_path_ref); + return; + } + + m_directory.SetString(resolve_path_ref.substr(0, dir_end)); + + size_t filename_begin = dir_end; + size_t root_dir_start = RootDirStart(resolve_path_ref, m_syntax); + while (filename_begin != llvm::StringRef::npos && + filename_begin < resolve_path_ref.size() && + filename_begin != root_dir_start && + IsPathSeparator(resolve_path_ref[filename_begin], m_syntax)) + ++filename_begin; + m_filename.SetString((filename_begin == llvm::StringRef::npos || + filename_begin >= resolve_path_ref.size()) + ? "." + : resolve_path_ref.substr(filename_begin)); +} + +void FileSpec::SetFile(llvm::StringRef path, bool resolve, + const llvm::Triple &Triple) { + return SetFile(path, resolve, + Triple.isOSWindows() ? ePathSyntaxWindows : ePathSyntaxPosix); +} + +//---------------------------------------------------------------------- +// 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 { + if (!FileEquals(rhs)) + return false; + if (DirectoryEquals(rhs)) + return true; + + // TODO: determine if we want to keep this code in here. + // The code below was added to handle a case where we were + // trying to set a file and line breakpoint and one path + // was resolved, and the other not and the directory was + // in a mount point that resolved to a more complete path: + // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling + // this out... + if (IsResolved() && rhs.IsResolved()) { + // Both paths are resolved, no need to look further... + return false; + } + + FileSpec resolved_lhs(*this); + + // If "this" isn't resolved, resolve it + if (!IsResolved()) { + if (resolved_lhs.ResolvePath()) { + // This path wasn't resolved but now it is. Check if the resolved + // directory is the same as our unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + m_is_resolved = (m_directory == resolved_lhs.m_directory); + } else + return false; + } + + FileSpec resolved_rhs(rhs); + if (!rhs.IsResolved()) { + if (resolved_rhs.ResolvePath()) { + // rhs's path wasn't resolved but now it is. Check if the resolved + // directory is the same as rhs's unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); + } else + return false; + } + + // If we reach this point in the code we were able to resolve both paths + // and since we only resolve the paths if the basenames are equal, then + // we can just check if both directories are equal... + return 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, + bool remove_backups) { + static ConstString g_dot_string("."); + static ConstString g_dot_dot_string(".."); + + // case sensitivity of equality test + const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); + + bool filenames_equal = ConstString::Equals(a.m_filename, + b.m_filename, + case_sensitive); + + // The only way two FileSpecs can be equal if their filenames are + // unequal is if we are removing backups and one or the other filename + // is a backup string: + + if (!filenames_equal && !remove_backups) + return false; + + bool last_component_is_dot = ConstString::Equals(a.m_filename, g_dot_string) + || ConstString::Equals(a.m_filename, + g_dot_dot_string) + || ConstString::Equals(b.m_filename, + g_dot_string) + || ConstString::Equals(b.m_filename, + g_dot_dot_string); + + if (!filenames_equal && !last_component_is_dot) + return false; + + if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) + return filenames_equal; + + if (remove_backups == false) + return a == b; + + if (a == b) + return true; + + return Equal(a.GetNormalizedPath(), b.GetNormalizedPath(), full, false); +} + +FileSpec FileSpec::GetNormalizedPath() const { + // Fast path. Do nothing if the path is not interesting. + if (!m_directory.GetStringRef().contains(".") && + !m_directory.GetStringRef().contains("//") && + m_filename.GetStringRef() != ".." && m_filename.GetStringRef() != ".") + return *this; + + llvm::SmallString<64> path, result; + const bool normalize = false; + GetPath(path, normalize); + llvm::StringRef rest(path); + + // We will not go below root dir. + size_t root_dir_start = RootDirStart(path, m_syntax); + const bool absolute = root_dir_start != llvm::StringRef::npos; + if (absolute) { + result += rest.take_front(root_dir_start + 1); + rest = rest.drop_front(root_dir_start + 1); + } else { + if (m_syntax == ePathSyntaxWindows && path.size() > 2 && path[1] == ':') { + result += rest.take_front(2); + rest = rest.drop_front(2); + } + } + + bool anything_added = false; + llvm::SmallVector<llvm::StringRef, 0> components, processed; + rest.split(components, '/', -1, false); + processed.reserve(components.size()); + for (auto component : components) { + if (component == ".") + continue; // Skip these. + if (component != "..") { + processed.push_back(component); + continue; // Regular file name. + } + if (!processed.empty()) { + processed.pop_back(); + continue; // Dots. Go one level up if we can. + } + if (absolute) + continue; // We're at the top level. Cannot go higher than that. Skip. + + result += component; // We're a relative path. We need to keep these. + result += '/'; + anything_added = true; + } + for (auto component : processed) { + result += component; + result += '/'; + anything_added = true; + } + if (anything_added) + result.pop_back(); // Pop last '/'. + else if (result.empty()) + result = "."; + + return FileSpec(result, false, m_syntax); +} + +//------------------------------------------------------------------ +// 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_syntax); + if (!m_filename && !path.empty() && path.back() != path_separator) + s->PutChar(path_separator); + } +} + +//------------------------------------------------------------------ +// Returns true if the file exists. +//------------------------------------------------------------------ +bool FileSpec::Exists() const { return llvm::sys::fs::exists(GetPath()); } + +bool FileSpec::Readable() const { + return GetPermissions() & llvm::sys::fs::perms::all_read; +} + +bool FileSpec::ResolveExecutableLocation() { + // CLEANUP: Use StringRef for string handling. + if (!m_directory) { + const char *file_cstr = m_filename.GetCString(); + if (file_cstr) { + const std::string file_str(file_cstr); + llvm::ErrorOr<std::string> error_or_path = + llvm::sys::findProgramByName(file_str); + if (!error_or_path) + return false; + std::string path = error_or_path.get(); + llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); + if (!dir_ref.empty()) { + // FindProgramByName returns "." if it can't find the file. + if (strcmp(".", dir_ref.data()) == 0) + return false; + + m_directory.SetCString(dir_ref.data()); + if (Exists()) + return true; + else { + // If FindProgramByName found the file, it returns the directory + + // filename in its return results. + // We need to separate them. + FileSpec tmp_file(dir_ref.data(), false); + if (tmp_file.Exists()) { + m_directory = tmp_file.m_directory; + return true; + } + } + } + } + } + + return false; +} + +bool FileSpec::ResolvePath() { + if (m_is_resolved) + return true; // We have already resolved this path + + // SetFile(...) will set m_is_resolved correctly if it can resolve the path + SetFile(GetPath(false), true); + return m_is_resolved; +} + +uint64_t FileSpec::GetByteSize() const { + uint64_t Size = 0; + if (llvm::sys::fs::file_size(GetPath(), Size)) + return 0; + return Size; +} + +FileSpec::PathSyntax FileSpec::GetPathSyntax() const { return m_syntax; } + +uint32_t FileSpec::GetPermissions() const { + namespace fs = llvm::sys::fs; + fs::file_status st; + if (fs::status(GetPath(), st, false)) + return fs::perms::perms_not_known; + + return st.permissions(); +} + +//------------------------------------------------------------------ +// Directory string get accessor. +//------------------------------------------------------------------ +ConstString &FileSpec::GetDirectory() { return m_directory; } + +//------------------------------------------------------------------ +// Directory string const get accessor. +//------------------------------------------------------------------ +const ConstString &FileSpec::GetDirectory() const { return m_directory; } + +//------------------------------------------------------------------ +// Filename string get accessor. +//------------------------------------------------------------------ +ConstString &FileSpec::GetFilename() { return m_filename; } + +//------------------------------------------------------------------ +// Filename string const get accessor. +//------------------------------------------------------------------ +const 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(NULL); +} + +void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, + bool denormalize) const { + path.append(m_directory.GetStringRef().begin(), + m_directory.GetStringRef().end()); + if (m_directory && m_filename && + !IsPathSeparator(m_directory.GetStringRef().back(), m_syntax)) + path.insert(path.end(), GetPreferredPathSeparator(m_syntax)); + path.append(m_filename.GetStringRef().begin(), + m_filename.GetStringRef().end()); + Normalize(path, m_syntax); + if (denormalize && !path.empty()) + Denormalize(path, m_syntax); +} + +ConstString FileSpec::GetFileNameExtension() const { + if (m_filename) { + const char *filename = m_filename.GetCString(); + const char *dot_pos = strrchr(filename, '.'); + if (dot_pos && dot_pos[1] != '\0') + return ConstString(dot_pos + 1); + } + return ConstString(); +} + +ConstString FileSpec::GetFileNameStrippingExtension() const { + const char *filename = m_filename.GetCString(); + if (filename == NULL) + return ConstString(); + + const char *dot_pos = strrchr(filename, '.'); + if (dot_pos == NULL) + return m_filename; + + return ConstString(filename, dot_pos - filename); +} + +//------------------------------------------------------------------ +// 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(); +} + +void FileSpec::EnumerateDirectory(llvm::StringRef dir_path, + bool find_directories, bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton) { + namespace fs = llvm::sys::fs; + std::error_code EC; + fs::recursive_directory_iterator Iter(dir_path, EC); + fs::recursive_directory_iterator End; + for (; Iter != End && !EC; Iter.increment(EC)) { + const auto &Item = *Iter; + fs::file_status Status; + if ((EC = Item.status(Status))) + break; + if (!find_files && fs::is_regular_file(Status)) + continue; + if (!find_directories && fs::is_directory(Status)) + continue; + if (!find_other && fs::is_other(Status)) + continue; + + FileSpec Spec(Item.path(), false); + auto Result = callback(callback_baton, Status.type(), Spec); + if (Result == eEnumerateDirectoryResultQuit) + return; + if (Result == eEnumerateDirectoryResultNext) { + // Default behavior is to recurse. Opt out if the callback doesn't want + // this behavior. + Iter.no_push(); + } + } +} + +FileSpec +FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const { + FileSpec ret = *this; + ret.AppendPathComponent(component); + return ret; +} + +FileSpec FileSpec::CopyByRemovingLastPathComponent() const { + // CLEANUP: Use StringRef for string handling. + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return FileSpec("", resolve); + if (m_directory.IsEmpty()) + return FileSpec("", resolve); + if (m_filename.IsEmpty()) { + const char *dir_cstr = m_directory.GetCString(); + const char *last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) + return FileSpec("", resolve); + if (last_slash_ptr == dir_cstr) + return FileSpec("/", resolve); + + size_t last_slash_pos = last_slash_ptr - dir_cstr + 1; + ConstString new_path(dir_cstr, last_slash_pos); + return FileSpec(new_path.GetCString(), resolve); + } else + return FileSpec(m_directory.GetCString(), resolve); +} + +ConstString FileSpec::GetLastPathComponent() const { + // CLEANUP: Use StringRef for string handling. + if (m_filename) + return m_filename; + if (m_directory) { + const char *dir_cstr = m_directory.GetCString(); + const char *last_slash_ptr = ::strrchr(dir_cstr, '/'); + if (last_slash_ptr == NULL) + return m_directory; + if (last_slash_ptr == dir_cstr) { + if (last_slash_ptr[1] == 0) + return ConstString(last_slash_ptr); + else + return ConstString(last_slash_ptr + 1); + } + if (last_slash_ptr[1] != 0) + return ConstString(last_slash_ptr + 1); + const char *penultimate_slash_ptr = last_slash_ptr; + while (*penultimate_slash_ptr) { + --penultimate_slash_ptr; + if (penultimate_slash_ptr == dir_cstr) + break; + if (*penultimate_slash_ptr == '/') + break; + } + ConstString result(penultimate_slash_ptr + 1, + last_slash_ptr - penultimate_slash_ptr); + return result; + } + return ConstString(); +} + +static std::string +join_path_components(FileSpec::PathSyntax syntax, + const std::vector<llvm::StringRef> components) { + std::string result; + for (size_t i = 0; i < components.size(); ++i) { + if (components[i].empty()) + continue; + result += components[i]; + if (i != components.size() - 1 && + !IsPathSeparator(components[i].back(), syntax)) + result += GetPreferredPathSeparator(syntax); + } + + return result; +} + +void FileSpec::PrependPathComponent(llvm::StringRef component) { + if (component.empty()) + return; + + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) { + SetFile(component, resolve); + return; + } + + std::string result = + join_path_components(m_syntax, {component, m_directory.GetStringRef(), + m_filename.GetStringRef()}); + SetFile(result, resolve, m_syntax); +} + +void FileSpec::PrependPathComponent(const FileSpec &new_path) { + return PrependPathComponent(new_path.GetPath(false)); +} + +void FileSpec::AppendPathComponent(llvm::StringRef component) { + if (component.empty()) + return; + + component = component.drop_while( + [this](char c) { return IsPathSeparator(c, m_syntax); }); + + std::string result = + join_path_components(m_syntax, {m_directory.GetStringRef(), + m_filename.GetStringRef(), component}); + + SetFile(result, false, m_syntax); +} + +void FileSpec::AppendPathComponent(const FileSpec &new_path) { + return AppendPathComponent(new_path.GetPath(false)); +} + +void FileSpec::RemoveLastPathComponent() { + // CLEANUP: Use StringRef for string handling. + + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) { + SetFile("", resolve); + return; + } + if (m_directory.IsEmpty()) { + SetFile("", resolve); + return; + } + if (m_filename.IsEmpty()) { + const char *dir_cstr = m_directory.GetCString(); + const char *last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) { + SetFile("", resolve); + return; + } + if (last_slash_ptr == dir_cstr) { + SetFile("/", resolve); + return; + } + size_t last_slash_pos = last_slash_ptr - dir_cstr + 1; + ConstString new_path(dir_cstr, last_slash_pos); + SetFile(new_path.GetCString(), resolve); + } else + SetFile(m_directory.GetCString(), resolve); +} +//------------------------------------------------------------------ +/// 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 { + const char *dir = m_directory.GetCString(); + llvm::StringRef directory(dir ? dir : ""); + + if (directory.size() > 0) { + if (PathSyntaxIsPosix(m_syntax)) { + // If the path doesn't start with '/' or '~', return true + switch (directory[0]) { + case '/': + case '~': + return false; + default: + return true; + } + } else { + if (directory.size() >= 2 && directory[1] == ':') + return false; + if (directory[0] == '/') + return false; + return true; + } + } else if (m_filename) { + // No directory, just a basename, return true + return true; + } + return false; +} + +bool FileSpec::IsAbsolute() const { return !FileSpec::IsRelative(); } + +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.GetPathSyntax()); + Stream << denormalized_dir; + Stream << GetPreferredPathSeparator(F.GetPathSyntax()); + } + + 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/contrib/llvm/tools/lldb/source/Utility/History.cpp b/contrib/llvm/tools/lldb/source/Utility/History.cpp new file mode 100644 index 0000000..10344b6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/History.cpp @@ -0,0 +1,24 @@ +//===-- History.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/History.h" + +// C Includes +#include <inttypes.h> +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void HistorySourceUInt::DumpHistoryEvent(Stream &strm, HistoryEvent event) { + strm.Printf("%s %" PRIu64, m_name.c_str(), (uint64_t)((uintptr_t)event)); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/IOObject.cpp b/contrib/llvm/tools/lldb/source/Utility/IOObject.cpp new file mode 100644 index 0000000..df7929c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/IOObject.cpp @@ -0,0 +1,15 @@ +//===-- IOObject.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/IOObject.h" + +using namespace lldb_private; + +const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1; +IOObject::~IOObject() = default; diff --git a/contrib/llvm/tools/lldb/source/Utility/JSON.cpp b/contrib/llvm/tools/lldb/source/Utility/JSON.cpp index 5b809c5..1520bc7 100644 --- a/contrib/llvm/tools/lldb/source/Utility/JSON.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/JSON.cpp @@ -9,10 +9,15 @@ #include "lldb/Utility/JSON.h" -#include "lldb/Core/StreamString.h" -#include "lldb/Host/StringConvert.h" +#include "lldb/Utility/Stream.h" // for Stream +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" + +#include <inttypes.h> // for PRIu64, PRId64 #include <limits.h> +#include <stddef.h> // for size_t +#include <utility> // for pair using namespace lldb_private; @@ -186,7 +191,7 @@ JSONValue::SP JSONArray::GetObject(Index i) { JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); } -JSONParser::JSONParser(const char *cstr) : StringExtractor(cstr) {} +JSONParser::JSONParser(llvm::StringRef data) : StringExtractor(data) {} JSONParser::Token JSONParser::GetToken(std::string &value) { StreamString error; @@ -241,7 +246,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { "error: an error occurred getting a character from offset %" PRIu64, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } else { const bool is_end_quote = escaped_ch == '"'; @@ -254,13 +259,13 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { "character 0x%4.4x at offset %" PRIu64, escaped_ch, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } } else if (is_end_quote) { return Token::String; } else if (is_null) { value = "error: missing end quote for string"; - return Token::Error; + return Token::Status; } } } @@ -311,7 +316,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { error.Printf("error: extra decimal point found at offset %" PRIu64, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } else { got_decimal_point = true; ++m_index; // Skip this character @@ -325,7 +330,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { "error: extra exponent character found at offset %" PRIu64, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } else { exp_index = m_index; ++m_index; // Skip this character @@ -341,7 +346,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { error.Printf("error: unexpected %c character at offset %" PRIu64, next_ch, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } break; @@ -363,7 +368,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { "at offset in float value \"%s\"", value.c_str()); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } } else { // No exponent, but we need at least one decimal after the decimal @@ -374,7 +379,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { error.Printf("error: no digits after decimal point \"%s\"", value.c_str()); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } } } else { @@ -385,14 +390,14 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { } else { error.Printf("error: no digits negate sign \"%s\"", value.c_str()); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } } } else { error.Printf("error: invalid number found at offset %" PRIu64, start_index); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } } break; default: @@ -402,7 +407,7 @@ JSONParser::Token JSONParser::GetToken(std::string &value) { " (around character '%c')", start_index, ch); value = std::move(error.GetString()); - return Token::Error; + return Token::Status; } int JSONParser::GetEscapedChar(bool &was_escaped) { @@ -512,23 +517,20 @@ JSONValue::SP JSONParser::ParseJSONValue() { case JSONParser::Token::Integer: { if (value.front() == '-') { - bool success = false; - int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success); - if (success) + int64_t sval = 0; + if (!llvm::StringRef(value).getAsInteger(0, sval)) return JSONValue::SP(new JSONNumber(sval)); } else { - bool success = false; - uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success); - if (success) + uint64_t uval = 0; + if (!llvm::StringRef(value).getAsInteger(0, uval)) return JSONValue::SP(new JSONNumber(uval)); } } break; case JSONParser::Token::Float: { - bool success = false; - double val = StringConvert::ToDouble(value.c_str(), 0.0, &success); - if (success) - return JSONValue::SP(new JSONNumber(val)); + double D; + if (!llvm::StringRef(value).getAsDouble(D)) + return JSONValue::SP(new JSONNumber(D)); } break; case JSONParser::Token::String: diff --git a/contrib/llvm/tools/lldb/source/Utility/LLDBAssert.cpp b/contrib/llvm/tools/lldb/source/Utility/LLDBAssert.cpp index 6f35dcd..48c1b69 100644 --- a/contrib/llvm/tools/lldb/source/Utility/LLDBAssert.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/LLDBAssert.cpp @@ -1,5 +1,4 @@ -//===--------------------- LLDBAssert.cpp --------------------------*- C++ -//-*-===// +//===--------------------- LLDBAssert.cpp ------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // diff --git a/contrib/llvm/tools/lldb/source/Utility/Log.cpp b/contrib/llvm/tools/lldb/source/Utility/Log.cpp new file mode 100644 index 0000000..a80b106 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Log.cpp @@ -0,0 +1,323 @@ +//===-- Log.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/VASPrintf.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" // for operator+, Twine +#include "llvm/ADT/iterator.h" // for iterator_facade_base + +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ManagedStatic.h" // for ManagedStatic +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#include <chrono> // for duration, system_clock, syst... +#include <cstdarg> +#include <mutex> +#include <utility> // for pair + +#include <assert.h> // for assert +#if defined(LLVM_ON_WIN32) +#include <process.h> // for getpid +#else +#include <unistd.h> +#endif + +using namespace lldb_private; + +llvm::ManagedStatic<Log::ChannelMap> Log::g_channel_map; + +void Log::ListCategories(llvm::raw_ostream &stream, const ChannelMap::value_type &entry) { + stream << llvm::formatv("Logging categories for '{0}':\n", entry.first()); + stream << " all - all available logging categories\n"; + stream << " default - default set of logging categories\n"; + for (const auto &category : entry.second.m_channel.categories) + stream << llvm::formatv(" {0} - {1}\n", category.name, + category.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::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::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); + if (!thread_name.empty()) + OS << 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()); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/Logging.cpp b/contrib/llvm/tools/lldb/source/Utility/Logging.cpp new file mode 100644 index 0000000..0bd6d66 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Logging.cpp @@ -0,0 +1,74 @@ +//===-- Logging.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Logging.h" +#include "lldb/Utility/Log.h" + +#include "llvm/ADT/ArrayRef.h" // for ArrayRef + +#include <stdarg.h> // for va_end, va_list, va_start + +using namespace lldb_private; + +static constexpr Log::Category g_categories[] = { + {{"api"}, {"log API calls and return values"}, LIBLLDB_LOG_API}, + {{"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::InitializeLog() { + 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); +} + + +void lldb_private::LogIfAnyCategoriesSet(uint32_t mask, const char *format, ...) { + if (Log *log = GetLogIfAnyCategoriesSet(mask)) { + va_list args; + va_start(args, format); + log->VAPrintf(format, args); + va_end(args); + } +} diff --git a/contrib/llvm/tools/lldb/source/Utility/ModuleCache.cpp b/contrib/llvm/tools/lldb/source/Utility/ModuleCache.cpp deleted file mode 100644 index 889cd8f..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/ModuleCache.cpp +++ /dev/null @@ -1,336 +0,0 @@ -//===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ModuleCache.h" - -#include "lldb/Core/Log.h" -#include "lldb/Core/Module.h" -#include "lldb/Core/ModuleList.h" -#include "lldb/Core/ModuleSpec.h" -#include "lldb/Host/File.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/LockFile.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/FileUtilities.h" - -#include <assert.h> - -#include <cstdio> - -using namespace lldb; -using namespace lldb_private; - -namespace { - -const char *kModulesSubdir = ".cache"; -const char *kLockDirName = ".lock"; -const char *kTempFileName = ".temp"; -const char *kTempSymFileName = ".symtemp"; -const char *kSymFileExtension = ".sym"; -const char *kFSIllegalChars = "\\/:*?\"<>|"; - -std::string GetEscapedHostname(const char *hostname) { - if (hostname == nullptr) - hostname = "unknown"; - std::string result(hostname); - size_t size = result.size(); - for (size_t i = 0; i < size; ++i) { - if ((result[i] >= 1 && result[i] <= 31) || - strchr(kFSIllegalChars, result[i]) != nullptr) - result[i] = '_'; - } - return result; -} - -class ModuleLock { -private: - File m_file; - std::unique_ptr<lldb_private::LockFile> m_lock; - FileSpec m_file_spec; - -public: - ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Error &error); - void Delete(); -}; - -FileSpec JoinPath(const FileSpec &path1, const char *path2) { - FileSpec result_spec(path1); - result_spec.AppendPathComponent(path2); - return result_spec; -} - -Error MakeDirectory(const FileSpec &dir_path) { - if (dir_path.Exists()) { - if (!dir_path.IsDirectory()) - return Error("Invalid existing path"); - - return Error(); - } - - return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault); -} - -FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { - const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); - return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); -} - -FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { - return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false); -} - -void DeleteExistingModule(const FileSpec &root_dir_spec, - const FileSpec &sysroot_module_path_spec) { - Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); - UUID module_uuid; - { - auto module_sp = - std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); - module_uuid = module_sp->GetUUID(); - } - - if (!module_uuid.IsValid()) - return; - - Error error; - ModuleLock lock(root_dir_spec, module_uuid, error); - if (error.Fail()) { - if (log) - log->Printf("Failed to lock module %s: %s", - module_uuid.GetAsString().c_str(), error.AsCString()); - } - - auto link_count = FileSystem::GetHardlinkCount(sysroot_module_path_spec); - if (link_count == -1) - return; - - if (link_count > 2) // module is referred by other hosts. - return; - - const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); - FileSystem::DeleteDirectory(module_spec_dir, true); - lock.Delete(); -} - -void DecrementRefExistingModule(const FileSpec &root_dir_spec, - const FileSpec &sysroot_module_path_spec) { - // Remove $platform/.cache/$uuid folder if nobody else references it. - DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); - - // Remove sysroot link. - FileSystem::Unlink(sysroot_module_path_spec); - - FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); - if (symfile_spec.Exists()) // delete module's symbol file if exists. - FileSystem::Unlink(symfile_spec); -} - -Error CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, - const char *hostname, - const FileSpec &platform_module_spec, - const FileSpec &local_module_spec, - bool delete_existing) { - const auto sysroot_module_path_spec = - JoinPath(JoinPath(root_dir_spec, hostname), - platform_module_spec.GetPath().c_str()); - if (sysroot_module_path_spec.Exists()) { - if (!delete_existing) - return Error(); - - DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); - } - - const auto error = MakeDirectory( - FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false)); - if (error.Fail()) - return error; - - return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec); -} - -} // namespace - -ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, - Error &error) { - const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); - error = MakeDirectory(lock_dir_spec); - if (error.Fail()) - return; - - m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); - m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite | - File::eOpenOptionCanCreate | - File::eOpenOptionCloseOnExec); - if (!m_file) { - error.SetErrorToErrno(); - return; - } - - m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor())); - error = m_lock->WriteLock(0, 1); - if (error.Fail()) - error.SetErrorStringWithFormat("Failed to lock file: %s", - error.AsCString()); -} - -void ModuleLock::Delete() { - if (!m_file) - return; - - m_file.Close(); - FileSystem::Unlink(m_file_spec); -} - -///////////////////////////////////////////////////////////////////////// - -Error ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, - const ModuleSpec &module_spec, const FileSpec &tmp_file, - const FileSpec &target_file) { - const auto module_spec_dir = - GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); - const auto module_file_path = - JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); - - const auto tmp_file_path = tmp_file.GetPath(); - const auto err_code = - llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); - if (err_code) - return Error("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), - module_file_path.GetPath().c_str(), - err_code.message().c_str()); - - const auto error = CreateHostSysRootModuleLink( - root_dir_spec, hostname, target_file, module_file_path, true); - if (error.Fail()) - return Error("Failed to create link to %s: %s", - module_file_path.GetPath().c_str(), error.AsCString()); - return Error(); -} - -Error ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, - const ModuleSpec &module_spec, - ModuleSP &cached_module_sp, bool *did_create_ptr) { - const auto find_it = - m_loaded_modules.find(module_spec.GetUUID().GetAsString()); - if (find_it != m_loaded_modules.end()) { - cached_module_sp = (*find_it).second.lock(); - if (cached_module_sp) - return Error(); - m_loaded_modules.erase(find_it); - } - - const auto module_spec_dir = - GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); - const auto module_file_path = JoinPath( - module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); - - if (!module_file_path.Exists()) - return Error("Module %s not found", module_file_path.GetPath().c_str()); - if (module_file_path.GetByteSize() != module_spec.GetObjectSize()) - return Error("Module %s has invalid file size", - module_file_path.GetPath().c_str()); - - // We may have already cached module but downloaded from an another host - in - // this case let's create a link to it. - auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, - module_spec.GetFileSpec(), - module_file_path, false); - if (error.Fail()) - return Error("Failed to create link to %s: %s", - module_file_path.GetPath().c_str(), error.AsCString()); - - auto cached_module_spec(module_spec); - cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 - // content hash instead of real UUID. - cached_module_spec.GetFileSpec() = module_file_path; - cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); - - error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, - nullptr, nullptr, did_create_ptr, false); - if (error.Fail()) - return error; - - FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); - if (symfile_spec.Exists()) - cached_module_sp->SetSymbolFileFileSpec(symfile_spec); - - m_loaded_modules.insert( - std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); - - return Error(); -} - -Error ModuleCache::GetAndPut(const FileSpec &root_dir_spec, - const char *hostname, - const ModuleSpec &module_spec, - const ModuleDownloader &module_downloader, - const SymfileDownloader &symfile_downloader, - lldb::ModuleSP &cached_module_sp, - bool *did_create_ptr) { - const auto module_spec_dir = - GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); - auto error = MakeDirectory(module_spec_dir); - if (error.Fail()) - return error; - - ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); - if (error.Fail()) - return Error("Failed to lock module %s: %s", - module_spec.GetUUID().GetAsString().c_str(), - error.AsCString()); - - const auto escaped_hostname(GetEscapedHostname(hostname)); - // Check local cache for a module. - error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, - cached_module_sp, did_create_ptr); - if (error.Success()) - return error; - - const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); - error = module_downloader(module_spec, tmp_download_file_spec); - llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); - if (error.Fail()) - return Error("Failed to download module: %s", error.AsCString()); - - // Put downloaded file into local module cache. - error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, - tmp_download_file_spec, module_spec.GetFileSpec()); - if (error.Fail()) - return Error("Failed to put module into cache: %s", error.AsCString()); - - tmp_file_remover.releaseFile(); - error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, - cached_module_sp, did_create_ptr); - if (error.Fail()) - return error; - - // Fetching a symbol file for the module - const auto tmp_download_sym_file_spec = - JoinPath(module_spec_dir, kTempSymFileName); - error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); - llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); - if (error.Fail()) - // Failed to download a symfile but fetching the module was successful. The - // module might - // contain the necessary symbols and the debugging is also possible without - // a symfile. - return Error(); - - error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, - tmp_download_sym_file_spec, - GetSymbolFileSpec(module_spec.GetFileSpec())); - if (error.Fail()) - return Error("Failed to put symbol file into cache: %s", error.AsCString()); - - tmp_symfile_remover.releaseFile(); - - FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); - cached_module_sp->SetSymbolFileFileSpec(symfile_spec); - return Error(); -} diff --git a/contrib/llvm/tools/lldb/source/Utility/ModuleCache.h b/contrib/llvm/tools/lldb/source/Utility/ModuleCache.h deleted file mode 100644 index 6faa5ff..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/ModuleCache.h +++ /dev/null @@ -1,76 +0,0 @@ -//===-- ModuleCache.h -------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef utility_ModuleCache_h_ -#define utility_ModuleCache_h_ - -#include "lldb/lldb-forward.h" -#include "lldb/lldb-types.h" - -#include "lldb/Core/Error.h" -#include "lldb/Host/File.h" -#include "lldb/Host/FileSpec.h" - -#include <functional> -#include <string> -#include <unordered_map> - -namespace lldb_private { - -class Module; -class UUID; - -//---------------------------------------------------------------------- -/// @class ModuleCache ModuleCache.h "Utility/ModuleCache.h" -/// @brief A module cache class. -/// -/// Caches locally modules that are downloaded from remote targets. -/// Each cached module maintains 2 views: -/// - UUID view: -/// /${CACHE_ROOT}/${PLATFORM_NAME}/.cache/${UUID}/${MODULE_FILENAME} -/// - Sysroot view: -/// /${CACHE_ROOT}/${PLATFORM_NAME}/${HOSTNAME}/${MODULE_FULL_FILEPATH} -/// -/// UUID views stores a real module file, whereas Sysroot view holds a symbolic -/// link to UUID-view file. -/// -/// Example: -/// UUID view : -/// /tmp/lldb/remote-linux/.cache/30C94DC6-6A1F-E951-80C3-D68D2B89E576-D5AE213C/libc.so.6 -/// Sysroot view: /tmp/lldb/remote-linux/ubuntu/lib/x86_64-linux-gnu/libc.so.6 -//---------------------------------------------------------------------- - -class ModuleCache { -public: - using ModuleDownloader = - std::function<Error(const ModuleSpec &, const FileSpec &)>; - using SymfileDownloader = - std::function<Error(const lldb::ModuleSP &, const FileSpec &)>; - - Error GetAndPut(const FileSpec &root_dir_spec, const char *hostname, - const ModuleSpec &module_spec, - const ModuleDownloader &module_downloader, - const SymfileDownloader &symfile_downloader, - lldb::ModuleSP &cached_module_sp, bool *did_create_ptr); - -private: - Error Put(const FileSpec &root_dir_spec, const char *hostname, - const ModuleSpec &module_spec, const FileSpec &tmp_file, - const FileSpec &target_file); - - Error Get(const FileSpec &root_dir_spec, const char *hostname, - const ModuleSpec &module_spec, lldb::ModuleSP &cached_module_sp, - bool *did_create_ptr); - - std::unordered_map<std::string, lldb::ModuleWP> m_loaded_modules; -}; - -} // namespace lldb_private - -#endif // utility_ModuleCache_h_ diff --git a/contrib/llvm/tools/lldb/source/Utility/NameMatches.cpp b/contrib/llvm/tools/lldb/source/Utility/NameMatches.cpp index 7b733d2..a76df3f 100644 --- a/contrib/llvm/tools/lldb/source/Utility/NameMatches.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/NameMatches.cpp @@ -7,38 +7,29 @@ // //===----------------------------------------------------------------------===// #include "lldb/Utility/NameMatches.h" -#include "lldb/Core/RegularExpression.h" +#include "lldb/Utility/RegularExpression.h" #include "llvm/ADT/StringRef.h" using namespace lldb_private; -bool lldb_private::NameMatches(llvm::StringRef name, NameMatchType match_type, +bool lldb_private::NameMatches(llvm::StringRef name, NameMatch match_type, llvm::StringRef match) { - if (match_type == eNameMatchIgnore) - return true; - - if (name == match) - return true; - - if (name.empty() || match.empty()) - return false; - switch (match_type) { - case eNameMatchIgnore: // This case cannot occur: tested before + case NameMatch::Ignore: return true; - case eNameMatchEquals: + case NameMatch::Equals: return name == match; - case eNameMatchContains: + case NameMatch::Contains: return name.contains(match); - case eNameMatchStartsWith: + case NameMatch::StartsWith: return name.startswith(match); - case eNameMatchEndsWith: + case NameMatch::EndsWith: return name.endswith(match); - case eNameMatchRegularExpression: { + case NameMatch::RegularExpression: { RegularExpression regex(match); return regex.Execute(name); - } break; + } } return false; } diff --git a/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp b/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp deleted file mode 100644 index 4d99a56..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp +++ /dev/null @@ -1,311 +0,0 @@ -//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Utility/PseudoTerminal.h" -#include "lldb/Host/Config.h" - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#if defined(TIOCSCTTY) -#include <sys/ioctl.h> -#endif - -#include "lldb/Host/PosixApi.h" - -#if defined(__ANDROID__) -int posix_openpt(int flags); -#endif - -using namespace lldb_utility; - -//---------------------------------------------------------------------- -// PseudoTerminal constructor -//---------------------------------------------------------------------- -PseudoTerminal::PseudoTerminal() - : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {} - -//---------------------------------------------------------------------- -// Destructor -// -// The destructor will close the master and slave file descriptors -// if they are valid and ownership has not been released using the -// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() -// member functions. -//---------------------------------------------------------------------- -PseudoTerminal::~PseudoTerminal() { - CloseMasterFileDescriptor(); - CloseSlaveFileDescriptor(); -} - -//---------------------------------------------------------------------- -// Close the master file descriptor if it is valid. -//---------------------------------------------------------------------- -void PseudoTerminal::CloseMasterFileDescriptor() { - if (m_master_fd >= 0) { - ::close(m_master_fd); - m_master_fd = invalid_fd; - } -} - -//---------------------------------------------------------------------- -// Close the slave file descriptor if it is valid. -//---------------------------------------------------------------------- -void PseudoTerminal::CloseSlaveFileDescriptor() { - if (m_slave_fd >= 0) { - ::close(m_slave_fd); - m_slave_fd = invalid_fd; - } -} - -//---------------------------------------------------------------------- -// Open the first available pseudo terminal with OFLAG as the -// permissions. The file descriptor is stored in this object and can -// be accessed with the MasterFileDescriptor() accessor. The -// ownership of the master file descriptor can be released using -// the ReleaseMasterFileDescriptor() accessor. If this object has -// a valid master files descriptor when its destructor is called, it -// will close the master file descriptor, therefore clients must -// call ReleaseMasterFileDescriptor() if they wish to use the master -// file descriptor after this object is out of scope or destroyed. -// -// RETURNS: -// True when successful, false indicating an error occurred. -//---------------------------------------------------------------------- -bool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str, - size_t error_len) { - if (error_str) - error_str[0] = '\0'; - -#if !defined(LLDB_DISABLE_POSIX) - // Open the master side of a pseudo terminal - m_master_fd = ::posix_openpt(oflag); - if (m_master_fd < 0) { - if (error_str) - ::strerror_r(errno, error_str, error_len); - return false; - } - - // Grant access to the slave pseudo terminal - if (::grantpt(m_master_fd) < 0) { - if (error_str) - ::strerror_r(errno, error_str, error_len); - CloseMasterFileDescriptor(); - return false; - } - - // Clear the lock flag on the slave pseudo terminal - if (::unlockpt(m_master_fd) < 0) { - if (error_str) - ::strerror_r(errno, error_str, error_len); - CloseMasterFileDescriptor(); - return false; - } - - return true; -#else - if (error_str) - ::snprintf(error_str, error_len, "%s", - "pseudo terminal not supported"); - return false; -#endif -} - -//---------------------------------------------------------------------- -// Open the slave pseudo terminal for the current master pseudo -// terminal. A master pseudo terminal should already be valid prior to -// calling this function (see OpenFirstAvailableMaster()). -// The file descriptor is stored this object's member variables and can -// be accessed via the GetSlaveFileDescriptor(), or released using the -// ReleaseSlaveFileDescriptor() member function. -// -// RETURNS: -// True when successful, false indicating an error occurred. -//---------------------------------------------------------------------- -bool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) { - if (error_str) - error_str[0] = '\0'; - - CloseSlaveFileDescriptor(); - - // Open the master side of a pseudo terminal - const char *slave_name = GetSlaveName(error_str, error_len); - - if (slave_name == nullptr) - return false; - - m_slave_fd = ::open(slave_name, oflag); - - if (m_slave_fd < 0) { - if (error_str) - ::strerror_r(errno, error_str, error_len); - return false; - } - - return true; -} - -//---------------------------------------------------------------------- -// Get the name of the slave pseudo terminal. A master pseudo terminal -// should already be valid prior to calling this function (see -// OpenFirstAvailableMaster()). -// -// RETURNS: -// NULL if no valid master pseudo terminal or if ptsname() fails. -// The name of the slave pseudo terminal as a NULL terminated C string -// that comes from static memory, so a copy of the string should be -// made as subsequent calls can change this value. -//---------------------------------------------------------------------- -const char *PseudoTerminal::GetSlaveName(char *error_str, - size_t error_len) const { - if (error_str) - error_str[0] = '\0'; - - if (m_master_fd < 0) { - if (error_str) - ::snprintf(error_str, error_len, "%s", - "master file descriptor is invalid"); - return nullptr; - } - const char *slave_name = ::ptsname(m_master_fd); - - if (error_str && slave_name == nullptr) - ::strerror_r(errno, error_str, error_len); - - return slave_name; -} - -//---------------------------------------------------------------------- -// Fork a child process and have its stdio routed to a pseudo terminal. -// -// In the parent process when a valid pid is returned, the master file -// descriptor can be used as a read/write access to stdio of the -// child process. -// -// In the child process the stdin/stdout/stderr will already be routed -// to the slave pseudo terminal and the master file descriptor will be -// closed as it is no longer needed by the child process. -// -// This class will close the file descriptors for the master/slave -// when the destructor is called, so be sure to call -// ReleaseMasterFileDescriptor() or ReleaseSlaveFileDescriptor() if any -// file descriptors are going to be used past the lifespan of this -// object. -// -// RETURNS: -// in the parent process: the pid of the child, or -1 if fork fails -// in the child process: zero -//---------------------------------------------------------------------- -lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) { - if (error_str) - error_str[0] = '\0'; - pid_t pid = LLDB_INVALID_PROCESS_ID; -#if !defined(LLDB_DISABLE_POSIX) - int flags = O_RDWR; - flags |= O_CLOEXEC; - if (OpenFirstAvailableMaster(flags, error_str, error_len)) { - // Successfully opened our master pseudo terminal - - pid = ::fork(); - if (pid < 0) { - // Fork failed - if (error_str) - ::strerror_r(errno, error_str, error_len); - } else if (pid == 0) { - // Child Process - ::setsid(); - - if (OpenSlave(O_RDWR, error_str, error_len)) { - // Successfully opened slave - - // Master FD should have O_CLOEXEC set, but let's close it just in - // case... - CloseMasterFileDescriptor(); - -#if defined(TIOCSCTTY) - // Acquire the controlling terminal - if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) { - if (error_str) - ::strerror_r(errno, error_str, error_len); - } -#endif - // Duplicate all stdio file descriptors to the slave pseudo terminal - if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) { - if (error_str && !error_str[0]) - ::strerror_r(errno, error_str, error_len); - } - - if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) { - if (error_str && !error_str[0]) - ::strerror_r(errno, error_str, error_len); - } - - if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) { - if (error_str && !error_str[0]) - ::strerror_r(errno, error_str, error_len); - } - } - } else { - // Parent Process - // Do nothing and let the pid get returned! - } - } -#endif - return pid; -} - -//---------------------------------------------------------------------- -// The master file descriptor accessor. This object retains ownership -// of the master file descriptor when this accessor is used. Use -// ReleaseMasterFileDescriptor() if you wish this object to release -// ownership of the master file descriptor. -// -// Returns the master file descriptor, or -1 if the master file -// descriptor is not currently valid. -//---------------------------------------------------------------------- -int PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; } - -//---------------------------------------------------------------------- -// The slave file descriptor accessor. -// -// Returns the slave file descriptor, or -1 if the slave file -// descriptor is not currently valid. -//---------------------------------------------------------------------- -int PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; } - -//---------------------------------------------------------------------- -// Release ownership of the master pseudo terminal file descriptor -// without closing it. The destructor for this class will close the -// master file descriptor if the ownership isn't released using this -// call and the master file descriptor has been opened. -//---------------------------------------------------------------------- -int PseudoTerminal::ReleaseMasterFileDescriptor() { - // Release ownership of the master pseudo terminal file - // descriptor without closing it. (the destructor for this - // class will close it otherwise!) - int fd = m_master_fd; - m_master_fd = invalid_fd; - return fd; -} - -//---------------------------------------------------------------------- -// Release ownership of the slave pseudo terminal file descriptor -// without closing it. The destructor for this class will close the -// slave file descriptor if the ownership isn't released using this -// call and the slave file descriptor has been opened. -//---------------------------------------------------------------------- -int PseudoTerminal::ReleaseSlaveFileDescriptor() { - // Release ownership of the slave pseudo terminal file - // descriptor without closing it (the destructor for this - // class will close it otherwise!) - int fd = m_slave_fd; - m_slave_fd = invalid_fd; - return fd; -} diff --git a/contrib/llvm/tools/lldb/source/Utility/Range.cpp b/contrib/llvm/tools/lldb/source/Utility/Range.cpp index 95f00e5..9d1d28e 100644 --- a/contrib/llvm/tools/lldb/source/Utility/Range.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/Range.cpp @@ -1,5 +1,4 @@ -//===--------------------- Range.cpp -----------------------------*- C++ -//-*-===// +//===--------------------- Range.cpp -----------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // @@ -10,6 +9,9 @@ #include "lldb/Utility/Range.h" +#include <algorithm> +#include <utility> + using namespace lldb_utility; Range::Range(const Range &rng) : m_low(rng.m_low), m_high(rng.m_high) { diff --git a/contrib/llvm/tools/lldb/source/Utility/RegisterNumber.cpp b/contrib/llvm/tools/lldb/source/Utility/RegisterNumber.cpp deleted file mode 100644 index 07dd223..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/RegisterNumber.cpp +++ /dev/null @@ -1,119 +0,0 @@ -//===--------------------- RegisterNumber.cpp -------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Utility/RegisterNumber.h" -#include "lldb/Target/RegisterContext.h" -#include "lldb/Target/Thread.h" - -using namespace lldb_private; - -RegisterNumber::RegisterNumber(lldb_private::Thread &thread, - lldb::RegisterKind kind, uint32_t num) - : m_reg_ctx_sp(thread.GetRegisterContext()), m_regnum(num), m_kind(kind), - m_kind_regnum_map(), m_name("") { - if (m_reg_ctx_sp.get()) { - const lldb_private::RegisterInfo *reginfo = - m_reg_ctx_sp->GetRegisterInfoAtIndex( - GetAsKind(lldb::eRegisterKindLLDB)); - if (reginfo && reginfo->name) { - m_name = reginfo->name; - } - } -} - -RegisterNumber::RegisterNumber() - : m_reg_ctx_sp(), m_regnum(LLDB_INVALID_REGNUM), - m_kind(lldb::kNumRegisterKinds), m_kind_regnum_map(), m_name(nullptr) {} - -void RegisterNumber::init(lldb_private::Thread &thread, lldb::RegisterKind kind, - uint32_t num) { - m_reg_ctx_sp = thread.GetRegisterContext(); - m_regnum = num; - m_kind = kind; - if (m_reg_ctx_sp.get()) { - const lldb_private::RegisterInfo *reginfo = - m_reg_ctx_sp->GetRegisterInfoAtIndex( - GetAsKind(lldb::eRegisterKindLLDB)); - if (reginfo && reginfo->name) { - m_name = reginfo->name; - } - } -} - -const RegisterNumber &RegisterNumber::operator=(const RegisterNumber &rhs) { - m_reg_ctx_sp = rhs.m_reg_ctx_sp; - m_regnum = rhs.m_regnum; - m_kind = rhs.m_kind; - for (auto it : rhs.m_kind_regnum_map) - m_kind_regnum_map[it.first] = it.second; - m_name = rhs.m_name; - return *this; -} - -bool RegisterNumber::operator==(RegisterNumber &rhs) { - if (IsValid() != rhs.IsValid()) - return false; - - if (m_kind == rhs.m_kind) { - if (m_regnum == rhs.m_regnum) - return true; - else - return false; - } - - uint32_t rhs_regnum = rhs.GetAsKind(m_kind); - if (rhs_regnum != LLDB_INVALID_REGNUM) { - if (m_regnum == rhs_regnum) - return true; - else - return false; - } - uint32_t lhs_regnum = GetAsKind(rhs.m_kind); - { - if (lhs_regnum == rhs.m_regnum) - return true; - else - return false; - } - return false; -} - -bool RegisterNumber::operator!=(RegisterNumber &rhs) { return !(*this == rhs); } - -bool RegisterNumber::IsValid() const { - return m_reg_ctx_sp.get() && m_kind != lldb::kNumRegisterKinds && - m_regnum != LLDB_INVALID_REGNUM; -} - -uint32_t RegisterNumber::GetAsKind(lldb::RegisterKind kind) { - if (m_regnum == LLDB_INVALID_REGNUM) - return LLDB_INVALID_REGNUM; - - if (kind == m_kind) - return m_regnum; - - Collection::iterator iter = m_kind_regnum_map.find(kind); - if (iter != m_kind_regnum_map.end()) { - return iter->second; - } - uint32_t output_regnum = LLDB_INVALID_REGNUM; - if (m_reg_ctx_sp && - m_reg_ctx_sp->ConvertBetweenRegisterKinds(m_kind, m_regnum, kind, - output_regnum) && - output_regnum != LLDB_INVALID_REGNUM) { - m_kind_regnum_map[kind] = output_regnum; - } - return output_regnum; -} - -uint32_t RegisterNumber::GetRegisterNumber() const { return m_regnum; } - -lldb::RegisterKind RegisterNumber::GetRegisterKind() const { return m_kind; } - -const char *RegisterNumber::GetName() { return m_name; } diff --git a/contrib/llvm/tools/lldb/source/Utility/RegularExpression.cpp b/contrib/llvm/tools/lldb/source/Utility/RegularExpression.cpp new file mode 100644 index 0000000..d58b315 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/RegularExpression.cpp @@ -0,0 +1,198 @@ +//===-- RegularExpression.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/RegularExpression.h" + +#include "llvm/ADT/StringRef.h" + +#include <string> + +//---------------------------------------------------------------------- +// Enable enhanced mode if it is available. This allows for things like +// \d for digit, \s for space, and many more, but it isn't available +// everywhere. +//---------------------------------------------------------------------- +#if defined(REG_ENHANCED) +#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED | REG_EXTENDED) +#else +#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED) +#endif + +using namespace lldb_private; + +RegularExpression::RegularExpression() : m_re(), m_comp_err(1), m_preg() { + memset(&m_preg, 0, sizeof(m_preg)); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(llvm::StringRef str) + : m_re(), m_comp_err(1), m_preg() { + memset(&m_preg, 0, sizeof(m_preg)); + Compile(str); +} + +RegularExpression::RegularExpression(const RegularExpression &rhs) { + memset(&m_preg, 0, sizeof(m_preg)); + Compile(rhs.GetText()); +} + +const RegularExpression &RegularExpression:: +operator=(const RegularExpression &rhs) { + if (&rhs != this) + Compile(rhs.GetText()); + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +// +// Any previously compiled regular expression contained in this +// object will be freed. +//---------------------------------------------------------------------- +RegularExpression::~RegularExpression() { Free(); } + +//---------------------------------------------------------------------- +// Compile a regular expression using the supplied regular +// expression text and flags. The compiled regular expression lives +// in this object so that it can be readily used for regular +// expression matches. Execute() can be called after the regular +// expression is compiled. Any previously compiled regular +// expression contained in this object will be freed. +// +// RETURNS +// True if the regular expression compiles successfully, false +// otherwise. +//---------------------------------------------------------------------- +bool RegularExpression::Compile(llvm::StringRef str) { + Free(); + + // regcomp() on darwin does not recognize "" as a valid regular expression, so + // we substitute it with an equivalent non-empty one. + m_re = str.empty() ? "()" : str; + m_comp_err = ::regcomp(&m_preg, m_re.c_str(), DEFAULT_COMPILE_FLAGS); + return m_comp_err == 0; +} + +//---------------------------------------------------------------------- +// Execute a regular expression match using the compiled regular +// expression that is already in this object against the match +// string "s". If any parens are used for regular expression +// matches "match_count" should indicate the number of regmatch_t +// values that are present in "match_ptr". The regular expression +// will be executed using the "execute_flags". +//--------------------------------------------------------------------- +bool RegularExpression::Execute(llvm::StringRef str, Match *match) const { + int err = 1; + if (m_comp_err == 0) { + // Argument to regexec must be null-terminated. + std::string reg_str = str; + if (match) { + err = ::regexec(&m_preg, reg_str.c_str(), match->GetSize(), + match->GetData(), 0); + } else { + err = ::regexec(&m_preg, reg_str.c_str(), 0, nullptr, 0); + } + } + + if (err != 0) { + // The regular expression didn't compile, so clear the matches + if (match) + match->Clear(); + return false; + } + return true; +} + +bool RegularExpression::Match::GetMatchAtIndex(llvm::StringRef s, uint32_t idx, + std::string &match_str) const { + llvm::StringRef match_str_ref; + if (GetMatchAtIndex(s, idx, match_str_ref)) { + match_str = match_str_ref.str(); + return true; + } + return false; +} + +bool RegularExpression::Match::GetMatchAtIndex( + llvm::StringRef s, uint32_t idx, llvm::StringRef &match_str) const { + if (idx < m_matches.size()) { + if (m_matches[idx].rm_eo == -1 && m_matches[idx].rm_so == -1) + return false; + + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) { + match_str = s.substr(m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool RegularExpression::Match::GetMatchSpanningIndices( + llvm::StringRef s, uint32_t idx1, uint32_t idx2, + llvm::StringRef &match_str) const { + if (idx1 < m_matches.size() && idx2 < m_matches.size()) { + if (m_matches[idx1].rm_so == m_matches[idx2].rm_eo) { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } else if (m_matches[idx1].rm_so < m_matches[idx2].rm_eo) { + match_str = s.substr(m_matches[idx1].rm_so, + m_matches[idx2].rm_eo - m_matches[idx1].rm_so); + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Returns true if the regular expression compiled and is ready +// for execution. +//---------------------------------------------------------------------- +bool RegularExpression::IsValid() const { return m_comp_err == 0; } + +//---------------------------------------------------------------------- +// Returns the text that was used to compile the current regular +// expression. +//---------------------------------------------------------------------- +llvm::StringRef RegularExpression::GetText() const { return m_re; } + +//---------------------------------------------------------------------- +// Free any contained compiled regular expressions. +//---------------------------------------------------------------------- +void RegularExpression::Free() { + if (m_comp_err == 0) { + m_re.clear(); + regfree(&m_preg); + // Set a compile error since we no longer have a valid regex + m_comp_err = 1; + } +} + +size_t RegularExpression::GetErrorAsCString(char *err_str, + size_t err_str_max_len) const { + if (m_comp_err == 0) { + if (err_str && err_str_max_len) + *err_str = '\0'; + return 0; + } + + return ::regerror(m_comp_err, &m_preg, err_str, err_str_max_len); +} + +bool RegularExpression::operator<(const RegularExpression &rhs) const { + return (m_re < rhs.m_re); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp b/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp index 805bcf2..a46213f 100644 --- a/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp @@ -14,7 +14,18 @@ #define _DARWIN_UNLIMITED_SELECT #endif -// C Includes +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-enumerations.h" // for ErrorType::eErrorTypePOSIX +#include "lldb/lldb-types.h" // for socket_t + +#include "llvm/ADT/DenseMap.h" // for DenseMapPair, DenseMap, Dense... +#include "llvm/ADT/Optional.h" // for Optional + +#include <algorithm> +#include <chrono> // for microseconds, seconds, steady... + #include <errno.h> #if defined(_WIN32) // Define NOMINMAX to avoid macros that conflict with std::min and std::max @@ -24,16 +35,6 @@ #include <sys/select.h> #endif -// C++ Includes -#include <algorithm> - -// Other libraries and framework includes -#include "llvm/ADT/SmallVector.h" - -// Project includes -#include "lldb/Core/Error.h" -#include "lldb/Utility/LLDBAssert.h" -#include "lldb/Utility/SelectHelper.h" SelectHelper::SelectHelper() : m_fd_map(), m_end_time() // Infinite timeout unless @@ -89,14 +90,14 @@ static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold, vold = std::max(*vold, vnew); } -lldb_private::Error SelectHelper::Select() { - lldb_private::Error error; +lldb_private::Status SelectHelper::Select() { + lldb_private::Status error; #ifdef _MSC_VER // 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::Error("Too many file descriptors for select()"); + return lldb_private::Status("Too many file descriptors for select()"); #endif llvm::Optional<lldb::socket_t> max_read_fd; diff --git a/contrib/llvm/tools/lldb/source/Utility/Status.cpp b/contrib/llvm/tools/lldb/source/Utility/Status.cpp new file mode 100644 index 0000000..b11a3db --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Status.cpp @@ -0,0 +1,309 @@ +//===-- Status.cpp -----------------------------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Status.h" + +#include "lldb/Utility/VASPrintf.h" +#include "lldb/lldb-defines.h" // for LLDB_GENERIC_ERROR +#include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType::eErr... +#include "llvm/ADT/SmallString.h" // for SmallString +#include "llvm/ADT/StringRef.h" // for StringRef +#include "llvm/Support/Errno.h" +#include "llvm/Support/FormatProviders.h" // for format_provider + +#include <cerrno> +#include <cstdarg> +#include <string> // for string +#include <system_error> + +#ifdef __APPLE__ +#include <mach/mach.h> +#endif + +#include <stdint.h> // for uint32_t + +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 Status &rhs) = default; + +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; +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Status &Status::operator=(uint32_t err) { + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); + return *this; +} + +Status::~Status() = default; + +//---------------------------------------------------------------------- +// 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; + + 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 accesssor 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 accesssor 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/contrib/llvm/tools/lldb/source/Utility/Stream.cpp b/contrib/llvm/tools/lldb/source/Utility/Stream.cpp new file mode 100644 index 0000000..04edc25 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Stream.cpp @@ -0,0 +1,576 @@ +//===-- Stream.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Stream.h" + +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/VASPrintf.h" +#include "llvm/ADT/SmallString.h" // for SmallString + +#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) {} + +Stream::Stream() + : m_flags(0), m_addr_size(4), m_byte_order(endian::InlHostByteOrder()), + m_indent_level(0) {} + +//------------------------------------------------------------------ +// 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) { + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) { + bool more = true; + while (more) { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + /* sign bit of byte is 2nd high order bit (0x40) */ + if ((sval == 0 && !(byte & 0x40)) || (sval == -1 && (byte & 0x40))) + more = false; + else + // more bytes to come + byte |= 0x80u; + bytes_written += Write(&byte, 1); + } + } else { + bytes_written = Printf("0x%" PRIi64, sval); + } + + return bytes_written; +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t Stream::PutULEB128(uint64_t uval) { + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) { + do { + + uint8_t byte = uval & 0x7fu; + uval >>= 7; + if (uval != 0) { + // more bytes to come + byte |= 0x80u; + } + bytes_written += Write(&byte, 1); + } while (uval != 0); + } else { + bytes_written = Printf("0x%" PRIx64, uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// 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 == NULL) + prefix = ""; + if (suffix == NULL) + 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, (uint64_t)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", (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", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int16_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream &Stream::operator<<(int16_t sval) { + Printf("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int32_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream &Stream::operator<<(int32_t sval) { + Printf("%i", (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); + + size_t length = 0; + for (char C : buf) + length += _PutHex8(C, false); + + va_end(args); + + return length; +} + +size_t Stream::PutNHex8(size_t n, uint8_t uvalue) { + size_t bytes_written = 0; + for (size_t i = 0; i < n; ++i) + bytes_written += _PutHex8(uvalue, false); + return bytes_written; +} + +size_t Stream::_PutHex8(uint8_t uvalue, bool add_prefix) { + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) { + bytes_written = 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]; + bytes_written = Write(nibble_chars, sizeof(nibble_chars)); + } + return bytes_written; +} + +size_t Stream::PutHex8(uint8_t uvalue) { return _PutHex8(uvalue, false); } + +size_t Stream::PutHex16(uint16_t uvalue, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } + return bytes_written; +} + +size_t Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } + return bytes_written; +} + +size_t Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) { + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } else { + for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) + bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); + } + return bytes_written; +} + +size_t Stream::PutMaxHex64(uint64_t uvalue, size_t byte_size, + lldb::ByteOrder byte_order) { + switch (byte_size) { + case 1: + return PutHex8((uint8_t)uvalue); + case 2: + return PutHex16((uint16_t)uvalue); + case 4: + return PutHex32((uint32_t)uvalue); + case 8: + return PutHex64(uvalue); + } + 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) { + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (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) + bytes_written += _PutHex8(src[i], false); + } else { + for (size_t i = src_len - 1; i < src_len; --i) + bytes_written += _PutHex8(src[i], false); + } + if (!binary_was_set) + m_flags.Clear(eBinary); + + return bytes_written; +} + +size_t Stream::PutBytesAsRawHex8(const void *s, size_t src_len, + ByteOrder src_byte_order, + ByteOrder dst_byte_order) { + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (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) + bytes_written += _PutHex8(src[i], false); + } else { + for (size_t i = src_len - 1; i < src_len; --i) + bytes_written += _PutHex8(src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +size_t Stream::PutCStringAsRawHex8(const char *s) { + size_t bytes_written = 0; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + do { + bytes_written += _PutHex8(*s, false); + ++s; + } while (*s); + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + +void Stream::UnitTest(Stream *s) { + s->PutHex8(0x12); + + s->PutChar(' '); + s->PutHex16(0x3456, endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderBig); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderLittle); + + const char *hola = "Hello World!!!"; + s->PutChar(' '); + s->PutCString(hola); + + s->PutChar(' '); + s->Write(hola, 5); + + s->PutChar(' '); + s->PutCStringAsRawHex8(hola); + + s->PutChar(' '); + s->PutCStringAsRawHex8("01234"); + + s->PutChar(' '); + s->Printf("pid=%i", 12733); + + s->PutChar(' '); + s->PrintfAsRawHex8("pid=%i", 12733); + s->PutChar('\n'); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/StreamCallback.cpp b/contrib/llvm/tools/lldb/source/Utility/StreamCallback.cpp new file mode 100644 index 0000000..9752843 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/StreamCallback.cpp @@ -0,0 +1,23 @@ +//===-- StreamCallback.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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/contrib/llvm/tools/lldb/source/Utility/StreamGDBRemote.cpp b/contrib/llvm/tools/lldb/source/Utility/StreamGDBRemote.cpp new file mode 100644 index 0000000..2620e37 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/StreamGDBRemote.cpp @@ -0,0 +1,46 @@ +//===-- StreamGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StreamGDBRemote.h" + +#include "lldb/Utility/Flags.h" // for Flags +#include "lldb/Utility/Stream.h" // for Stream::::eBinary + +#include <stdio.h> + +using namespace lldb; +using namespace lldb_private; + +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 = (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; +} diff --git a/contrib/llvm/tools/lldb/source/Utility/StreamString.cpp b/contrib/llvm/tools/lldb/source/Utility/StreamString.cpp new file mode 100644 index 0000000..75f58de --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/StreamString.cpp @@ -0,0 +1,64 @@ +//===-- StreamString.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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::Write(const void *s, size_t length) { + m_packet.append(reinterpret_cast<const char *>(s), length); + return length; +} + +void StreamString::Clear() { m_packet.clear(); } + +bool StreamString::Empty() const { return GetSize() == 0; } + +size_t StreamString::GetSize() const { return m_packet.size(); } + +size_t StreamString::GetSizeOfLastLine() const { + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) { + return length; + } else { + ++last_line_begin_pos; + return length - last_line_begin_pos; + } +} + +llvm::StringRef StreamString::GetString() const { return m_packet; } + +void StreamString::FillLastLineToColumn(uint32_t column, char fill_char) { + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) { + last_line_begin_pos = 0; + } else { + ++last_line_begin_pos; + } + + const size_t line_columns = length - last_line_begin_pos; + if (column > line_columns) { + m_packet.append(column - line_columns, fill_char); + } +} diff --git a/contrib/llvm/tools/lldb/source/Utility/StringExtractor.cpp b/contrib/llvm/tools/lldb/source/Utility/StringExtractor.cpp index d8ba397..cf5c7e2 100644 --- a/contrib/llvm/tools/lldb/source/Utility/StringExtractor.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/StringExtractor.cpp @@ -9,13 +9,11 @@ #include "lldb/Utility/StringExtractor.h" -// C Includes -#include <stdlib.h> - -// C++ Includes #include <tuple> -// Other libraries and framework includes -// Project includes + +#include <ctype.h> // for isxdigit, isspace +#include <stdlib.h> +#include <string.h> // for memset static inline int xdigit_to_sint(char ch) { if (ch >= 'a' && ch <= 'f') @@ -282,6 +280,15 @@ uint64_t StringExtractor::GetHexMaxU64(bool little_endian, 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; diff --git a/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.cpp b/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.cpp index dd13be9..8e50c01 100644 --- a/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -7,14 +7,11 @@ // //===----------------------------------------------------------------------===// -// C Includes -#include <string.h> - -// C++ Includes -// Other libraries and framework includes -// Project includes #include "Utility/StringExtractorGDBRemote.h" +#include <ctype.h> // for isxdigit +#include <string.h> + StringExtractorGDBRemote::ResponseType StringExtractorGDBRemote::GetResponseType() const { if (m_packet.empty()) @@ -22,8 +19,18 @@ StringExtractorGDBRemote::GetResponseType() const { switch (m_packet[0]) { case 'E': - if (m_packet.size() == 3 && isxdigit(m_packet[1]) && isxdigit(m_packet[2])) - return eError; + 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': @@ -89,8 +96,14 @@ StringExtractorGDBRemote::GetServerPacketType() const { 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; + case 'S': if (PACKET_MATCHES("QStartNoAckMode")) return eServerPacketType_QStartNoAckMode; @@ -285,6 +298,16 @@ StringExtractorGDBRemote::GetServerPacketType() const { 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': @@ -427,8 +450,8 @@ bool StringExtractorGDBRemote::IsNormalResponse() const { } bool StringExtractorGDBRemote::IsErrorResponse() const { - return GetResponseType() == eError && m_packet.size() == 3 && - isxdigit(m_packet[1]) && isxdigit(m_packet[2]); + return GetResponseType() == eError && isxdigit(m_packet[1]) && + isxdigit(m_packet[2]); } uint8_t StringExtractorGDBRemote::GetError() { @@ -439,6 +462,23 @@ uint8_t StringExtractorGDBRemote::GetError() { 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() diff --git a/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.h b/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.h index ce12660..f4ed642 100644 --- a/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.h +++ b/contrib/llvm/tools/lldb/source/Utility/StringExtractorGDBRemote.h @@ -10,12 +10,14 @@ #ifndef utility_StringExtractorGDBRemote_h_ #define utility_StringExtractorGDBRemote_h_ -// C Includes -// C++ Includes -#include <string> -// Other libraries and framework includes -// Project includes +#include "lldb/Utility/Status.h" #include "lldb/Utility/StringExtractor.h" +#include "llvm/ADT/StringRef.h" // for StringRef + +#include <string> + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint8_t class StringExtractorGDBRemote : public StringExtractor { public: @@ -71,6 +73,7 @@ public: eServerPacketType_qGetWorkingDir, eServerPacketType_qFileLoadAddress, eServerPacketType_QEnvironment, + eServerPacketType_QEnableErrorStrings, eServerPacketType_QLaunchArch, eServerPacketType_QSetDisableASLR, eServerPacketType_QSetDetachOnError, @@ -96,6 +99,7 @@ public: // debug server packages eServerPacketType_QEnvironmentHexEncoded, eServerPacketType_QListThreadsInStopReply, + eServerPacketType_QPassSignals, eServerPacketType_QRestoreRegisterState, eServerPacketType_QSaveRegisterState, eServerPacketType_QSetLogging, @@ -162,6 +166,12 @@ public: eServerPacketType__M, eServerPacketType__m, eServerPacketType_notify, // '%' notification + + eServerPacketType_jTraceStart, + eServerPacketType_jTraceBufferRead, + eServerPacketType_jTraceMetaRead, + eServerPacketType_jTraceStop, + eServerPacketType_jTraceConfigRead, }; ServerPacketType GetServerPacketType() const; @@ -182,6 +192,8 @@ public: // digits. Otherwise the error encoded in XX is returned. uint8_t GetError(); + lldb_private::Status GetStatus(); + size_t GetEscapedBinaryData(std::string &str); protected: diff --git a/contrib/llvm/tools/lldb/source/Utility/StringLexer.cpp b/contrib/llvm/tools/lldb/source/Utility/StringLexer.cpp index ec18f049..d5c7fc6 100644 --- a/contrib/llvm/tools/lldb/source/Utility/StringLexer.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/StringLexer.cpp @@ -1,5 +1,4 @@ -//===--------------------- StringLexer.cpp -----------------------*- C++ -//-*-===// +//===--------------------- StringLexer.cpp -----------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // @@ -74,10 +73,6 @@ void StringLexer::PutBack(Size s) { m_position -= s; } -bool StringLexer::HasAny(Character c) { - return m_data.find(c, m_position) != std::string::npos; -} - std::string StringLexer::GetUnlexed() { return std::string(m_data, m_position); } diff --git a/contrib/llvm/tools/lldb/source/Utility/StringList.cpp b/contrib/llvm/tools/lldb/source/Utility/StringList.cpp new file mode 100644 index 0000000..190cb9d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/StringList.cpp @@ -0,0 +1,268 @@ +//===-- StringList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StringList.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" // for Stream +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/ArrayRef.h" // for ArrayRef, makeArrayRef + +#include <algorithm> // for min +#include <stdint.h> // for SIZE_MAX, uint32_t +#include <string.h> // for size_t, strcspn, NULL + +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) { + size_t len = strings.GetSize(); + + for (size_t i = 0; i < len; ++i) + m_strings.push_back(strings.GetStringAtIndex(i)); +} + +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 NULL; +} + +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(); } + +void StringList::LongestCommonPrefix(std::string &common_prefix) { + common_prefix.clear(); + 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); + } + common_prefix = prefix; +} + +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; +} + +size_t StringList::AutoComplete(llvm::StringRef s, StringList &matches, + size_t &exact_idx) const { + matches.Clear(); + exact_idx = SIZE_MAX; + if (s.empty()) { + // No string, so it matches everything + matches = *this; + return matches.GetSize(); + } + + const size_t s_len = s.size(); + const size_t num_strings = m_strings.size(); + + for (size_t i = 0; i < num_strings; ++i) { + if (m_strings[i].find(s) == 0) { + if (exact_idx == SIZE_MAX && m_strings[i].size() == s_len) + exact_idx = matches.GetSize(); + matches.AppendString(m_strings[i]); + } + } + return matches.GetSize(); +} + +void StringList::LogDump(Log *log, const char *name) { + if (!log) + return; + + StreamString strm; + if (name) + strm.Printf("Begin %s:\n", name); + for (const auto &s : m_strings) { + strm.Indent(); + strm.Printf("%s\n", s.c_str()); + } + if (name) + strm.Printf("End %s.\n", name); + + LLDB_LOGV(log, "{0}", strm.GetData()); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/StructuredData.cpp b/contrib/llvm/tools/lldb/source/Utility/StructuredData.cpp new file mode 100644 index 0000000..9fc0535 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/StructuredData.cpp @@ -0,0 +1,286 @@ +//===---------------------StructuredData.cpp ---------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" // for Stream +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/STLExtras.h" // for make_unique +#include <cerrno> +#include <cstdlib> +#include <inttypes.h> +#include <limits> // for numeric_limits + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Functions that use a JSONParser to parse JSON into StructuredData +//---------------------------------------------------------------------- +static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser); +static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser); +static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser); + +StructuredData::ObjectSP +StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) { + StructuredData::ObjectSP return_sp; + if (!input_spec.Exists()) { + error.SetErrorStringWithFormatv("input file {0} does not exist.", + input_spec); + return 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; + } + + JSONParser json_parser(buffer_or_error.get()->getBuffer()); + return_sp = ParseJSONValue(json_parser); + return return_sp; +} + +static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser) { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed by the time this function is called + auto dict_up = llvm::make_unique<StructuredData::Dictionary>(); + + std::string value; + std::string key; + while (1) { + JSONParser::Token token = json_parser.GetToken(value); + + if (token == JSONParser::Token::String) { + key.swap(value); + token = json_parser.GetToken(value); + if (token == JSONParser::Token::Colon) { + StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); + if (value_sp) + dict_up->AddItem(key, value_sp); + else + break; + } + } else if (token == JSONParser::Token::ObjectEnd) { + return StructuredData::ObjectSP(dict_up.release()); + } else if (token == JSONParser::Token::Comma) { + continue; + } else { + break; + } + } + return StructuredData::ObjectSP(); +} + +static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser) { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed + // by the time this function is called + auto array_up = llvm::make_unique<StructuredData::Array>(); + + std::string value; + std::string key; + while (1) { + StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); + if (value_sp) + array_up->AddItem(value_sp); + else + break; + + JSONParser::Token token = json_parser.GetToken(value); + if (token == JSONParser::Token::Comma) { + continue; + } else if (token == JSONParser::Token::ArrayEnd) { + return StructuredData::ObjectSP(array_up.release()); + } else { + break; + } + } + return StructuredData::ObjectSP(); +} + +static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser) { + std::string value; + const JSONParser::Token token = json_parser.GetToken(value); + switch (token) { + case JSONParser::Token::ObjectStart: + return ParseJSONObject(json_parser); + + case JSONParser::Token::ArrayStart: + return ParseJSONArray(json_parser); + + case JSONParser::Token::Integer: { + uint64_t uval; + if (llvm::to_integer(value, uval, 0)) + return std::make_shared<StructuredData::Integer>(uval); + } break; + + case JSONParser::Token::Float: { + double val; + if (llvm::to_float(value, val)) + return std::make_shared<StructuredData::Float>(val); + } break; + + case JSONParser::Token::String: + return std::make_shared<StructuredData::String>(value); + + case JSONParser::Token::True: + case JSONParser::Token::False: + return std::make_shared<StructuredData::Boolean>(token == + JSONParser::Token::True); + + case JSONParser::Token::Null: + return std::make_shared<StructuredData::Null>(); + + default: + break; + } + return StructuredData::ObjectSP(); +} + +StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) { + JSONParser json_parser(json_text.c_str()); + StructuredData::ObjectSP object_sp = ParseJSONValue(json_parser); + return object_sp; +} + +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.size() == 0) { + return this->shared_from_this(); + } + errno = 0; + uint64_t val = strtoul(match.second.str().c_str(), NULL, 10); + if (errno == 0) { + return this->GetAsArray()->GetItemAtIndex(val); + } + return ObjectSP(); + } + + return this->shared_from_this(); +} + +void StructuredData::Object::DumpToStdout(bool pretty_print) const { + StreamString stream; + Dump(stream, pretty_print); + llvm::outs() << stream.GetString(); +} + +void StructuredData::Array::Dump(Stream &s, bool pretty_print) const { + bool first = true; + s << "["; + if (pretty_print) { + s << "\n"; + s.IndentMore(); + } + for (const auto &item_sp : m_items) { + if (first) { + first = false; + } else { + s << ","; + if (pretty_print) + s << "\n"; + } + + if (pretty_print) + s.Indent(); + item_sp->Dump(s, pretty_print); + } + if (pretty_print) { + s.IndentLess(); + s.EOL(); + s.Indent(); + } + s << "]"; +} + +void StructuredData::Integer::Dump(Stream &s, bool pretty_print) const { + s.Printf("%" PRIu64, m_value); +} + +void StructuredData::Float::Dump(Stream &s, bool pretty_print) const { + s.Printf("%lg", m_value); +} + +void StructuredData::Boolean::Dump(Stream &s, bool pretty_print) const { + if (m_value == true) + s.PutCString("true"); + else + s.PutCString("false"); +} + +void StructuredData::String::Dump(Stream &s, bool pretty_print) const { + std::string quoted; + const size_t strsize = m_value.size(); + for (size_t i = 0; i < strsize; ++i) { + char ch = m_value[i]; + if (ch == '"' || ch == '\\') + quoted.push_back('\\'); + quoted.push_back(ch); + } + s.Printf("\"%s\"", quoted.c_str()); +} + +void StructuredData::Dictionary::Dump(Stream &s, bool pretty_print) const { + bool first = true; + s << "{"; + if (pretty_print) { + s << "\n"; + s.IndentMore(); + } + for (const auto &pair : m_dict) { + if (first) + first = false; + else { + s << ","; + if (pretty_print) + s << "\n"; + } + if (pretty_print) + s.Indent(); + s << "\"" << pair.first.AsCString() << "\" : "; + pair.second->Dump(s, pretty_print); + } + if (pretty_print) { + s.IndentLess(); + s.EOL(); + s.Indent(); + } + s << "}"; +} + +void StructuredData::Null::Dump(Stream &s, bool pretty_print) const { + s << "null"; +} + +void StructuredData::Generic::Dump(Stream &s, bool pretty_print) const { + s << "0x" << m_object; +} diff --git a/contrib/llvm/tools/lldb/source/Utility/TaskPool.cpp b/contrib/llvm/tools/lldb/source/Utility/TaskPool.cpp index f66f7bf..d33f23c 100644 --- a/contrib/llvm/tools/lldb/source/Utility/TaskPool.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/TaskPool.cpp @@ -9,6 +9,10 @@ #include "lldb/Utility/TaskPool.h" +#include <cstdint> // for uint32_t +#include <queue> // for queue +#include <thread> // for thread + namespace { class TaskPoolImpl { public: @@ -69,3 +73,26 @@ void TaskPoolImpl::Worker(TaskPoolImpl *pool) { f(); } } + +void TaskMapOverInt(size_t begin, size_t end, + const llvm::function_ref<void(size_t)> &func) { + std::atomic<size_t> idx{begin}; + size_t num_workers = + std::min<size_t>(end, std::thread::hardware_concurrency()); + + auto wrapper = [&idx, end, &func]() { + while (true) { + size_t i = idx.fetch_add(1); + if (i >= end) + break; + func(i); + } + }; + + std::vector<std::future<void>> futures; + futures.reserve(num_workers); + for (size_t i = 0; i < num_workers; i++) + futures.push_back(TaskPool::AddTask(wrapper)); + for (size_t i = 0; i < num_workers; i++) + futures[i].wait(); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/TildeExpressionResolver.cpp b/contrib/llvm/tools/lldb/source/Utility/TildeExpressionResolver.cpp new file mode 100644 index 0000000..64a7711 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/TildeExpressionResolver.cpp @@ -0,0 +1,95 @@ +//===--------------------- TildeExpressionResolver.cpp ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/TildeExpressionResolver.h" + +#include <assert.h> // for assert +#include <system_error> // for error_code + +#include "llvm/ADT/STLExtras.h" // for any_of +#include "llvm/ADT/SmallVector.h" // for SmallVectorImpl +#include "llvm/Config/llvm-config.h" // for LLVM_ON_WIN32 +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" // for fs + +#if !defined(LLVM_ON_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(LLVM_ON_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()) != NULL) { + 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/contrib/llvm/tools/lldb/source/Utility/Timer.cpp b/contrib/llvm/tools/lldb/source/Utility/Timer.cpp new file mode 100644 index 0000000..fe77874 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/Timer.cpp @@ -0,0 +1,132 @@ +//===-- Timer.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Utility/Timer.h" +#include "lldb/Utility/Stream.h" + +#include <algorithm> +#include <map> +#include <mutex> +#include <utility> // for pair +#include <vector> + +#include <assert.h> // for assert +#include <stdarg.h> // for va_end, va_list, va_start +#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); + 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(); +} + +void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; } + +/* binary function predicate: + * - returns whether a person is less than another person + */ + +typedef std::pair<const char *, uint64_t> TimerEntry; + +static bool CategoryMapIteratorSortCriterion(const TimerEntry &lhs, + const TimerEntry &rhs) { + return lhs.second > rhs.second; +} + +void Timer::ResetCategoryTimes() { + for (Category *i = g_categories; i; i = i->m_next) + i->m_nanos.store(0, std::memory_order_release); +} + +void Timer::DumpCategoryTimes(Stream *s) { + std::vector<TimerEntry> sorted; + for (Category *i = g_categories; i; i = i->m_next) { + uint64_t nanos = i->m_nanos.load(std::memory_order_acquire); + if (nanos) + sorted.push_back(std::make_pair(i->m_name, nanos)); + } + if (sorted.empty()) + return; // Later code will break without any elements. + + // Sort by time + std::sort(sorted.begin(), sorted.end(), CategoryMapIteratorSortCriterion); + + for (const auto &timer : sorted) + s->Printf("%.9f sec for %s\n", timer.second / 1000000000., timer.first); +} diff --git a/contrib/llvm/tools/lldb/source/Utility/UUID.cpp b/contrib/llvm/tools/lldb/source/Utility/UUID.cpp new file mode 100644 index 0000000..b47f8b5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/UUID.cpp @@ -0,0 +1,227 @@ +//===-- UUID.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/UUID.h" + +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" + +// C Includes +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +namespace lldb_private { + +UUID::UUID() : m_num_uuid_bytes(16) { ::memset(m_uuid, 0, sizeof(m_uuid)); } + +UUID::UUID(const UUID &rhs) { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy(m_uuid, rhs.m_uuid, sizeof(m_uuid)); +} + +UUID::UUID(const void *uuid_bytes, uint32_t num_uuid_bytes) { + SetBytes(uuid_bytes, num_uuid_bytes); +} + +const UUID &UUID::operator=(const UUID &rhs) { + if (this != &rhs) { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy(m_uuid, rhs.m_uuid, sizeof(m_uuid)); + } + return *this; +} + +UUID::~UUID() {} + +void UUID::Clear() { + m_num_uuid_bytes = 16; + ::memset(m_uuid, 0, sizeof(m_uuid)); +} + +const void *UUID::GetBytes() const { return m_uuid; } + +std::string UUID::GetAsString(const char *separator) const { + std::string result; + char buf[256]; + if (!separator) + separator = "-"; + const uint8_t *u = (const uint8_t *)GetBytes(); + if (sizeof(buf) > + (size_t)snprintf(buf, sizeof(buf), "%2.2X%2.2X%2.2X%2.2X%s%2.2X%2.2X%s%2." + "2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%2.2X%" + "2.2X%2.2X%2.2X", + u[0], u[1], u[2], u[3], separator, u[4], u[5], separator, + u[6], u[7], separator, u[8], u[9], separator, u[10], + u[11], u[12], u[13], u[14], u[15])) { + result.append(buf); + if (m_num_uuid_bytes == 20) { + if (sizeof(buf) > (size_t)snprintf(buf, sizeof(buf), + "%s%2.2X%2.2X%2.2X%2.2X", separator, + u[16], u[17], u[18], u[19])) + result.append(buf); + } + } + return result; +} + +void UUID::Dump(Stream *s) const { + const uint8_t *u = (const uint8_t *)GetBytes(); + s->Printf("%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%" + "2.2X%2.2X%2.2X%2.2X", + u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], + u[11], u[12], u[13], u[14], u[15]); + if (m_num_uuid_bytes == 20) { + s->Printf("-%2.2X%2.2X%2.2X%2.2X", u[16], u[17], u[18], u[19]); + } +} + +bool UUID::SetBytes(const void *uuid_bytes, uint32_t num_uuid_bytes) { + if (uuid_bytes) { + switch (num_uuid_bytes) { + case 20: + m_num_uuid_bytes = 20; + break; + case 16: + m_num_uuid_bytes = 16; + m_uuid[16] = m_uuid[17] = m_uuid[18] = m_uuid[19] = 0; + break; + default: + // Unsupported UUID byte size + m_num_uuid_bytes = 0; + break; + } + + if (m_num_uuid_bytes > 0) { + ::memcpy(m_uuid, uuid_bytes, m_num_uuid_bytes); + return true; + } + } + ::memset(m_uuid, 0, sizeof(m_uuid)); + return false; +} + +size_t UUID::GetByteSize() { return m_num_uuid_bytes; } + +bool UUID::IsValid() const { + return m_uuid[0] || m_uuid[1] || m_uuid[2] || m_uuid[3] || m_uuid[4] || + m_uuid[5] || m_uuid[6] || m_uuid[7] || m_uuid[8] || m_uuid[9] || + m_uuid[10] || m_uuid[11] || m_uuid[12] || m_uuid[13] || m_uuid[14] || + m_uuid[15] || m_uuid[16] || m_uuid[17] || m_uuid[18] || m_uuid[19]; +} + +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, + ValueType &uuid_bytes, + uint32_t &bytes_decoded, + uint32_t num_uuid_bytes) { + ::memset(uuid_bytes, 0, sizeof(uuid_bytes)); + size_t uuid_byte_idx = 0; + 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[uuid_byte_idx] = (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_byte_idx == 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; + } + } + + // Clear trailing bytes to 0. + for (uint32_t i = uuid_byte_idx; i < sizeof(ValueType); i++) + uuid_bytes[i] = 0; + bytes_decoded = uuid_byte_idx; + 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(); + + uint32_t bytes_decoded = 0; + llvm::StringRef rest = + UUID::DecodeUUIDBytesFromString(p, m_uuid, bytes_decoded, num_uuid_bytes); + + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (bytes_decoded == num_uuid_bytes) { + m_num_uuid_bytes = num_uuid_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::SetFromCString(const char *cstr, uint32_t num_uuid_bytes) { + if (cstr == NULL) + return 0; + + return SetFromStringRef(cstr, num_uuid_bytes); +} +} + +bool lldb_private::operator==(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) == 0; +} + +bool lldb_private::operator!=(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) != 0; +} + +bool lldb_private::operator<(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) < 0; +} + +bool lldb_private::operator<=(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) <= 0; +} + +bool lldb_private::operator>(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) > 0; +} + +bool lldb_private::operator>=(const lldb_private::UUID &lhs, + const lldb_private::UUID &rhs) { + return ::memcmp(lhs.GetBytes(), rhs.GetBytes(), + sizeof(lldb_private::UUID::ValueType)) >= 0; +} diff --git a/contrib/llvm/tools/lldb/source/Utility/UriParser.cpp b/contrib/llvm/tools/lldb/source/Utility/UriParser.cpp index a1d6e4c..bb57211 100644 --- a/contrib/llvm/tools/lldb/source/Utility/UriParser.cpp +++ b/contrib/llvm/tools/lldb/source/Utility/UriParser.cpp @@ -7,16 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "Utility/UriParser.h" +#include "lldb/Utility/UriParser.h" -// C Includes +#include <string> -// C++ Includes -#include <cstring> - -// Other libraries and framework includes -// Project includes -#include "lldb/Host/StringConvert.h" +#include <stdint.h> +#include <tuple> using namespace lldb_private; diff --git a/contrib/llvm/tools/lldb/source/Utility/UriParser.h b/contrib/llvm/tools/lldb/source/Utility/UriParser.h deleted file mode 100644 index 7ebf76f..0000000 --- a/contrib/llvm/tools/lldb/source/Utility/UriParser.h +++ /dev/null @@ -1,36 +0,0 @@ -//===-- UriParser.h ---------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef utility_UriParser_h_ -#define utility_UriParser_h_ - -// C Includes -// C++ Includes - -// Other libraries and framework includes -#include "llvm/ADT/StringRef.h" - -// Project includes - -class UriParser { -public: - // Parses - // RETURN VALUE - // if url is valid, function returns true and - // scheme/hostname/port/path are set to the parsed values - // port it set to -1 if it is not included in the URL - // - // if the url is invalid, function returns false and - // output parameters remain unchanged - static bool Parse(llvm::StringRef uri, llvm::StringRef &scheme, - llvm::StringRef &hostname, int &port, - llvm::StringRef &path); -}; - -#endif // utility_UriParser_h_ diff --git a/contrib/llvm/tools/lldb/source/Utility/UserID.cpp b/contrib/llvm/tools/lldb/source/Utility/UserID.cpp new file mode 100644 index 0000000..e65b8fa --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/UserID.cpp @@ -0,0 +1,21 @@ +//===-- UserID.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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/contrib/llvm/tools/lldb/source/Utility/VASprintf.cpp b/contrib/llvm/tools/lldb/source/Utility/VASprintf.cpp new file mode 100644 index 0000000..e950fb7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/VASprintf.cpp @@ -0,0 +1,56 @@ +//===-- VASPrintf.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/VASPrintf.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" // for SmallVectorImpl +#include "llvm/ADT/StringRef.h" // for StringRef + +#include <assert.h> // for assert +#include <stdarg.h> // for va_end, va_list, va_copy +#include <stdio.h> // for vsnprintf, size_t + +bool lldb_private::VASprintf(llvm::SmallVectorImpl<char> &buf, const char *fmt, + va_list args) { + llvm::SmallString<16> error("<Encoding error>"); + bool result = true; + + // Copy in case our first call to vsnprintf doesn't fit into our buffer + va_list copy_args; + va_copy(copy_args, args); + + buf.resize(buf.capacity()); + // Write up to `capacity` bytes, ignoring the current size. + int length = ::vsnprintf(buf.data(), buf.size(), fmt, args); + if (length < 0) { + buf = error; + result = false; + goto finish; + } + + if (size_t(length) >= buf.size()) { + // The error formatted string didn't fit into our buffer, resize it + // to the exact needed size, and retry + buf.resize(length + 1); + length = ::vsnprintf(buf.data(), buf.size(), fmt, copy_args); + if (length < 0) { + buf = error; + result = false; + goto finish; + } + assert(size_t(length) < buf.size()); + } + buf.resize(length); + +finish: + va_end(args); + va_end(copy_args); + return result; +} diff --git a/contrib/llvm/tools/lldb/source/Utility/VMRange.cpp b/contrib/llvm/tools/lldb/source/Utility/VMRange.cpp new file mode 100644 index 0000000..105b1a5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/VMRange.cpp @@ -0,0 +1,69 @@ +//===-- VMRange.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/VMRange.h" + +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-types.h" // for addr_t + +#include <algorithm> +#include <iterator> // for distance +#include <vector> // for const_iterator + +#include <stddef.h> // for size_t +#include <stdint.h> // for UINT32_MAX, uint32_t + +using namespace lldb; +using namespace lldb_private; + +bool VMRange::ContainsValue(const VMRange::collection &coll, + lldb::addr_t value) { + ValueInRangeUnaryPredicate in_range_predicate(value); + return llvm::find_if(coll, in_range_predicate) != coll.end(); +} + +bool VMRange::ContainsRange(const VMRange::collection &coll, + const VMRange &range) { + RangeInRangeUnaryPredicate in_range_predicate(range); + return llvm::find_if(coll, in_range_predicate) != 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); +} |