mib updated this revision to Diff 216747.
mib marked 16 inline comments as done.
mib added a comment.
Add doxygen documentation.
Move `ArgumentMetadata` struct inside `BreakpointInjectedSite`.
Change string format for logging.
Make `$__lldb_create_args_struct` generation architecture-independent.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D66249/new/
https://reviews.llvm.org/D66249
Files:
lldb/include/lldb/API/SBBreakpoint.h
lldb/include/lldb/API/SBBreakpointLocation.h
lldb/include/lldb/Breakpoint/Breakpoint.h
lldb/include/lldb/Breakpoint/BreakpointInjectedSite.h
lldb/include/lldb/Breakpoint/BreakpointLocation.h
lldb/include/lldb/Breakpoint/BreakpointOptions.h
lldb/include/lldb/Breakpoint/BreakpointSite.h
lldb/include/lldb/Core/Disassembler.h
lldb/include/lldb/Core/Opcode.h
lldb/include/lldb/Expression/Expression.h
lldb/include/lldb/Expression/ExpressionVariable.h
lldb/include/lldb/Expression/LLVMUserExpression.h
lldb/include/lldb/Symbol/VariableList.h
lldb/include/lldb/Target/ABI.h
lldb/include/lldb/Target/ExecutionContextScope.h
lldb/include/lldb/Target/Process.h
lldb/include/lldb/Target/Target.h
lldb/include/lldb/lldb-enumerations.h
lldb/include/lldb/lldb-forward.h
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/Makefile
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/TestFastConditionalBreakpoints.py
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/main.c
lldb/scripts/interface/SBBreakpoint.i
lldb/scripts/interface/SBBreakpointLocation.i
lldb/source/API/SBBreakpoint.cpp
lldb/source/API/SBBreakpointLocation.cpp
lldb/source/Breakpoint/Breakpoint.cpp
lldb/source/Breakpoint/BreakpointInjectedSite.cpp
lldb/source/Breakpoint/BreakpointLocation.cpp
lldb/source/Breakpoint/BreakpointOptions.cpp
lldb/source/Breakpoint/BreakpointSite.cpp
lldb/source/Breakpoint/CMakeLists.txt
lldb/source/Commands/CommandObjectBreakpoint.cpp
lldb/source/Commands/Options.td
lldb/source/Expression/LLVMUserExpression.cpp
lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
lldb/source/Symbol/ClangASTContext.cpp
lldb/source/Target/Process.cpp
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -1612,9 +1612,22 @@
return error;
}
+lldb::break_id_t
+Process::FallbackToRegularBreakpointSite(const BreakpointLocationSP &owner,
+ bool use_hardware, Log *log,
+ const char *error) {
+ LLDB_LOG(log, error);
+ LLDB_LOG(log, "Disabling JIT-ed condition and falling back to regular "
+ "conditional breakpoint");
+ owner->SetInjectCondition(false);
+ return CreateBreakpointSite(owner, use_hardware);
+}
+
lldb::break_id_t
Process::CreateBreakpointSite(const BreakpointLocationSP &owner,
bool use_hardware) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+
addr_t load_addr = LLDB_INVALID_ADDRESS;
bool show_error = true;
@@ -1675,11 +1688,72 @@
if (bp_site_sp) {
bp_site_sp->AddOwner(owner);
+
+ if (owner->GetInjectCondition()) {
+ BreakpointSite *bp_site = bp_site_sp.get();
+
+ BreakpointInjectedSite *bp_jitted_site_sp =
+ llvm::dyn_cast<BreakpointInjectedSite>(bp_site);
+ bp_jitted_site_sp->BuildConditionExpression();
+ }
+
owner->SetBreakpointSite(bp_site_sp);
return bp_site_sp->GetID();
} else {
- bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner,
- load_addr, use_hardware));
+ if (owner->GetInjectCondition() && GetABI()->ImplementsJIT()) {
+ // Build user expression's IR from condition
+ BreakpointInjectedSite *bp_injected_site = new BreakpointInjectedSite(
+ &m_breakpoint_site_list, owner, load_addr);
+
+ std::string error;
+ // Setup a call before the copied instructions
+ if (!bp_injected_site->BuildConditionExpression()) {
+ error = "FCB: Couldn't build the condition expression";
+ return FallbackToRegularBreakpointSite(owner, use_hardware, log,
+ error.c_str());
+ }
+
+ size_t instrs_size = SaveInstructions(owner->GetAddress());
+
+ if (!instrs_size) {
+ error = "FCB: Couldn't save instructions";
+
+ return FallbackToRegularBreakpointSite(owner, use_hardware, log,
+ error.c_str());
+ }
+
+ const lldb::addr_t cond_expr_addr =
+ bp_injected_site->GetConditionExpressionAddress();
+ const lldb::addr_t util_func_addr =
+ bp_injected_site->GetUtilityFunctionAddress();
+
+ if (!GetABI()->SetupFastConditionalBreakpointTrampoline(
+ instrs_size, m_overriden_instructions, load_addr,
+ util_func_addr, cond_expr_addr)) {
+ error = "FCB: Couldn't setup trampoline";
+
+ return FallbackToRegularBreakpointSite(owner, use_hardware, log,
+ error.c_str());
+ }
+
+ addr_t trap_addr = bp_injected_site->GetTrapAddress();
+
+ if (trap_addr == LLDB_INVALID_ADDRESS) {
+ error = "FCB: Couldn't get trap address";
+ return FallbackToRegularBreakpointSite(owner, use_hardware, log,
+ error.c_str());
+ }
+
+ // bp_site_sp.reset(bp_jitted_site);
+ bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner,
+ trap_addr, use_hardware));
+
+ bp_site_sp->AddOwner(owner);
+ } else {
+
+ bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner,
+ load_addr, use_hardware));
+ }
if (bp_site_sp) {
Status error = EnableBreakpointSite(bp_site_sp.get());
if (error.Success()) {
@@ -1702,6 +1776,84 @@
return LLDB_INVALID_BREAK_ID;
}
+size_t Process::SaveInstructions(Address &address) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+
+ TargetSP target_sp = m_target_wp.lock();
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool prefer_file_cache = true;
+
+ Function *function = address.CalculateSymbolContextFunction();
+
+ if (!function) {
+ LLDB_LOG(log, "JIT: No function in the SymbolContext");
+ return 0;
+ }
+
+ lldb::addr_t addr = address.GetCallableLoadAddress(target_sp.get());
+
+ const AddressRange disasm_range(addr,
+ function->GetAddressRange().GetByteSize());
+
+ DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
+ target_sp->GetArchitecture(), plugin_name, flavor, this, disasm_range,
+ prefer_file_cache);
+
+ if (!disassembler_sp) {
+ LLDB_LOG(log, "JIT: Couldn't disassemble '{}' function",
+ function->GetName());
+ return 0;
+ }
+
+ InstructionList *instructions = &disassembler_sp->GetInstructionList();
+
+ DataExtractor data;
+
+ size_t instructions_count = 0;
+ size_t instructions_size = 0;
+
+ for (size_t i = 0; i < instructions->GetSize(); i++) {
+ InstructionSP instruction = instructions->GetInstructionAtIndex(i);
+
+ instruction->GetData(data);
+ uint32_t size = instruction->Decode(*disassembler_sp.get(), data, 0);
+
+ if (instructions_size < GetABI()->GetJumpSize()) {
+ instructions_size += size;
+ instructions_count++;
+ }
+
+ const ExecutionContext exe_ctx(this);
+
+ LLDB_LOGV(log, "%#llx <+%llu>: %s, %s\t\t(%u)",
+ instruction->GetAddress().GetLoadAddress(target_sp.get()),
+ instruction->GetAddress().GetOffset(),
+ instruction->GetMnemonic(&exe_ctx),
+ instruction->GetOperands(&exe_ctx), size);
+ }
+
+ LLDB_LOGV(log, "JIT: Instruction count: {}", instructions_count);
+
+ Status error;
+ m_overriden_instructions = std::calloc(instructions_size, sizeof(uint8_t));
+
+ if (!m_overriden_instructions) {
+ LLDB_LOG(log, "JIT: Couldn't allocate instruction buffer");
+ return 0;
+ }
+
+ size_t memory_read =
+ ReadMemory(addr, m_overriden_instructions, instructions_size, error);
+
+ if (memory_read != instructions_size || error.Fail()) {
+ LLDB_LOG(log, "JIT: Couldn't copy instruction to buffer");
+ return 0;
+ }
+
+ return memory_read;
+}
+
void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id,
lldb::user_id_t owner_loc_id,
BreakpointSiteSP &bp_site_sp) {
Index: lldb/source/Symbol/ClangASTContext.cpp
===================================================================
--- lldb/source/Symbol/ClangASTContext.cpp
+++ lldb/source/Symbol/ClangASTContext.cpp
@@ -250,7 +250,7 @@
// We have an object already read from process memory,
// so just extract VTable pointer from it
- DataExtractor data;
+ lldb_private::DataExtractor data;
Status err;
auto size = valobj.GetData(data, err);
if (err.Fail() || vbtable_ptr_offset + data.GetAddressByteSize() > size)
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -324,7 +324,7 @@
bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec);
- DataExtractor GetAuxvData() override;
+ lldb_private::DataExtractor GetAuxvData() override;
StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid);
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4041,7 +4041,7 @@
return error;
}
-DataExtractor ProcessGDBRemote::GetAuxvData() {
+lldb_private::DataExtractor ProcessGDBRemote::GetAuxvData() {
DataBufferSP buf;
if (m_gdb_comm.GetQXferAuxvReadSupported()) {
std::string response_string;
@@ -4051,7 +4051,7 @@
buf = std::make_shared<DataBufferHeap>(response_string.c_str(),
response_string.length());
}
- return DataExtractor(buf, GetByteOrder(), GetAddressByteSize());
+ return lldb_private::DataExtractor(buf, GetByteOrder(), GetAddressByteSize());
}
StructuredData::ObjectSP
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -549,6 +549,8 @@
ResetDeclMap(exe_ctx, m_result_delegate, keep_result_in_memory);
auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); });
+ if (m_options.GetInjectCondition())
+ on_exit.release();
if (!DeclMap()->WillParse(exe_ctx, m_materializer_up.get())) {
diagnostic_manager.PutString(
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
@@ -76,7 +76,7 @@
bool RewriteExpression(DiagnosticManager &diagnostic_manager) override;
- /// Ready an already-parsed expression for execution, possibly evaluating it
+ /// Read an already-parsed expression for execution, possibly evaluating it
/// statically.
///
/// \param[out] func_addr
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
@@ -304,6 +304,8 @@
CompilerDeclContext &namespace_decl,
unsigned int current_id);
+ ExpressionVariableList &GetStructMembers(void) { return m_struct_members; }
+
private:
ExpressionVariableList
m_found_entities; ///< All entities that were looked up for the parser.
Index: lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
===================================================================
--- lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
+++ lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
@@ -9,6 +9,53 @@
#ifndef liblldb_ABISysV_x86_64_h_
#define liblldb_ABISysV_x86_64_h_
+#define X64_JMP_OPCODE (0xE9)
+#define X64_JMP_SIZE (5)
+#define X64_CALL_OPCODE (0xE8)
+#define X64_CALL_SIZE (5)
+
+#define X64_PUSH_OPCODE (0x50)
+#define X64_POP_OPCODE (0x58)
+
+#define X64_MOV_OPCODE (0x89)
+#define X64_MOV_SIZE (3)
+
+#define X64_REXB_OPCODE (0x41)
+#define X64_REXW_OPCODE (0x48)
+
+#define X64_SAVED_REGS (16)
+#define X64_VOLATILE_REGS (8)
+
+#define X64_REGS_CTX_STR "typedef struct {\n" \
+ " intptr_t r15;\n" \
+ " intptr_t r14;\n" \
+ " intptr_t r13;\n" \
+ " intptr_t r12;\n" \
+ " intptr_t r11;\n" \
+ " intptr_t r10;\n" \
+ " intptr_t r9;\n" \
+ " intptr_t r8;\n" \
+ " intptr_t rdi;\n" \
+ " intptr_t rsi;\n" \
+ " intptr_t rbp;\n" \
+ " intptr_t rsp;\n" \
+ " intptr_t rbx;\n" \
+ " intptr_t rdx;\n" \
+ " intptr_t rcx;\n" \
+ " intptr_t rax;\n" \
+ "} register_context;\n\n"
+
+#define X64_MACH_TYPES " typedef unsigned int uint32_t;\n" \
+ " typedef unsigned long long uint64_t ;\n" \
+ " typedef unsigned long uintptr_t;\n" \
+ " typedef uint32_t mach_port_t;\n" \
+ " typedef mach_port_t vm_map_t;\n" \
+ " typedef int kern_return_t;\n" \
+ " typedef uintptr_t vm_offset_t;\n" \
+ " typedef uint64_t mach_vm_address_t;\n" \
+ " typedef vm_offset_t vm_address_t;\n" \
+ " typedef uint64_t mach_vm_size_t;\n"
+
#include "lldb/Target/ABI.h"
#include "lldb/lldb-private.h"
@@ -72,6 +119,45 @@
bool GetPointerReturnRegister(const char *&name) override;
+ /// Allocate a memory stub for the fast condition breakpoint trampoline, and
+ /// build it by saving the register context, calling the argument structure
+ /// builder, passing the resulting structure to the condition checker,
+ /// restoring the register context, running the copied instructions and]
+ /// jumping back to the user source code.
+ ///
+ /// \param[in] instrs_size
+ /// The size in bytes of the copied instructions.
+ ///
+ /// \param[in] data
+ /// The copied instructions buffer.
+ ///
+ /// \param[in] jmp_addr
+ /// The address of the source .
+ ///
+ /// \param[in] util_func_addr
+ /// The address of the JIT-ed argument structure builder.
+ ///
+ /// \param[in] cond_expr_addr
+ /// The address of the JIT-ed condition checker.
+ ///
+ bool SetupFastConditionalBreakpointTrampoline(
+ size_t instrs_size, void *data, lldb::addr_t &jmp_addr,
+ lldb::addr_t util_func_addr, lldb::addr_t cond_expr_addr) override;
+
+ llvm::ArrayRef<uint8_t> GetJumpOpcode() override { return X64_JMP_OPCODE; }
+
+ size_t GetJumpSize() override { return X64_JMP_SIZE; }
+
+ llvm::ArrayRef<uint8_t> GetCallOpcode() override { return X64_JMP_OPCODE; }
+
+ size_t GetCallSize() override { return X64_JMP_SIZE; }
+
+ llvm::StringRef GetRegisterContextAsString() { return X64_REGS_CTX_STR; }
+
+ llvm::StringRef GetMachTypesAsString() { return X64_MACH_TYPES; }
+
+ bool ImplementsJIT() override { return true; }
+
// Static Functions
static void Initialize();
Index: lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
===================================================================
--- lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
+++ lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
@@ -18,6 +18,7 @@
#include "lldb/Core/ValueObjectConstResult.h"
#include "lldb/Core/ValueObjectMemory.h"
#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Expression/IRMemoryMap.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
@@ -214,6 +215,148 @@
return true;
}
+bool ABISysV_x86_64::SetupFastConditionalBreakpointTrampoline(
+ size_t instrs_size, void *data, addr_t &jmp_addr, addr_t util_func_addr,
+ addr_t cond_expr_addr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+
+ ProcessSP process_sp = m_process_wp.lock();
+
+ // Copy saved instructions to the inferior memory buffer.
+ Status error;
+ uint8_t alignment = 8;
+ uint32_t permission =
+ ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable;
+ IRMemoryMap::AllocationPolicy policy = IRMemoryMap::eAllocationPolicyMirror;
+
+ IRMemoryMap memory_map(process_sp->GetTarget().shared_from_this());
+
+ size_t context_size = X64_SAVED_REGS + X64_VOLATILE_REGS;
+
+ size_t expected_trampoline_size = context_size; // Save registers
+ expected_trampoline_size += X64_MOV_SIZE; // Pass register addr as arg
+ expected_trampoline_size += X64_CALL_SIZE; // Create arg struct
+ expected_trampoline_size += X64_MOV_SIZE; // Pass arg struct to jit expr
+ expected_trampoline_size += X64_CALL_SIZE; // Call jit expr
+ expected_trampoline_size += context_size; // Restore registers
+ expected_trampoline_size += instrs_size; // Run copied instrs
+ expected_trampoline_size += X64_JMP_SIZE; // Return to user code
+
+ addr_t trampoline_addr = memory_map.Malloc(
+ expected_trampoline_size, alignment, permission, policy, true, error);
+
+ if (trampoline_addr == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "JIT: Couldn't allocate trampoline buffer");
+ return false;
+ }
+
+ size_t trampoline_size = 0;
+
+ uint8_t trampoline_buffer[expected_trampoline_size];
+
+ uint8_t regs_ctx[context_size];
+ // Save registers
+ for (size_t i = 0; i < 8; i++)
+ regs_ctx[i] = X64_PUSH_OPCODE + i;
+ for (size_t i = 0; i < 8; i++) {
+ size_t offset = X64_VOLATILE_REGS + 2 * i;
+ regs_ctx[offset] = X64_REXB_OPCODE;
+ regs_ctx[offset + 1] = X64_PUSH_OPCODE + i;
+ }
+ std::memcpy(trampoline_buffer, ®s_ctx, context_size);
+ trampoline_size += context_size;
+
+ // Pass the register address as an argument.
+ uint8_t mov_buffer[X64_MOV_SIZE];
+ mov_buffer[0] = X64_REXW_OPCODE;
+ mov_buffer[1] = X64_MOV_OPCODE;
+ // %rsp SIB Bytes
+ mov_buffer[2] = 0xE7;
+ std::memcpy(&trampoline_buffer[trampoline_size], &mov_buffer, X64_MOV_SIZE);
+ trampoline_size += X64_MOV_SIZE;
+
+ // Call to create_arg_struct.
+ uint8_t call_buffer[X64_CALL_SIZE];
+ uint32_t call_offset =
+ -X64_CALL_SIZE - trampoline_size - trampoline_addr + util_func_addr;
+ call_buffer[0] = X64_CALL_OPCODE;
+ std::memcpy(&call_buffer[1], &call_offset, sizeof(uint32_t));
+ std::memcpy(&trampoline_buffer[trampoline_size], call_buffer, X64_CALL_SIZE);
+ trampoline_size += X64_CALL_SIZE;
+
+ // Pass the argument structure to condition checker.
+ // %rdi SIB Bytes
+ mov_buffer[2] = 0xC7;
+ std::memcpy(&trampoline_buffer[trampoline_size], &mov_buffer, X64_MOV_SIZE);
+ trampoline_size += X64_MOV_SIZE;
+
+ // Copy condition checker call in trampoline buffer.
+ call_offset =
+ -X64_CALL_SIZE - trampoline_size - trampoline_addr + cond_expr_addr;
+ call_buffer[0] = X64_CALL_OPCODE;
+ std::memcpy(&call_buffer[1], &call_offset, sizeof(uint32_t));
+ std::memcpy(&trampoline_buffer[trampoline_size], call_buffer, X64_CALL_SIZE);
+ trampoline_size += X64_CALL_SIZE;
+
+ // Restore registers.
+ for (size_t i = 0; i < 8; i++) {
+ regs_ctx[2 * i] = X64_REXB_OPCODE;
+ regs_ctx[2 * i + 1] = X64_POP_OPCODE + X64_VOLATILE_REGS - i - 1;
+ }
+ for (size_t i = 0; i < 8; i++)
+ regs_ctx[X64_SAVED_REGS + i] = X64_POP_OPCODE + X64_VOLATILE_REGS - i - 1;
+ std::memcpy(&trampoline_buffer[trampoline_size], ®s_ctx, context_size);
+ trampoline_size += context_size;
+
+ // Copy saved instruction in trampoline buffer.
+ std::memcpy(&trampoline_buffer[trampoline_size], data, instrs_size);
+ trampoline_size += instrs_size;
+
+ // Copy jump back instruction in trampoline buffer.
+ uint8_t jmp_buffer[X64_JMP_SIZE];
+ uint32_t jmp_offset = jmp_addr - trampoline_addr - trampoline_size;
+
+ jmp_buffer[0] = X64_JMP_OPCODE;
+ std::memcpy(&jmp_buffer[1], &jmp_offset, sizeof(uint32_t));
+ std::memcpy(&trampoline_buffer[trampoline_size], jmp_buffer, X64_JMP_SIZE);
+ trampoline_size += X64_JMP_SIZE;
+
+ if (trampoline_size != expected_trampoline_size) {
+ LLDB_LOG(log, "JIT: Trampoline size ({}) is not the one expected ({})",
+ trampoline_size, expected_trampoline_size);
+ return false;
+ }
+
+ size_t written_bytes = process_sp->WriteMemory(
+ trampoline_addr, &trampoline_buffer, trampoline_size, error);
+
+ if (written_bytes != trampoline_size || error.Fail()) {
+ LLDB_LOG(log, "JIT: Couldn't write trampoline buffer to inferior");
+ return false;
+ }
+
+ // Overwrite current instruction with JMP.
+ jmp_offset = trampoline_addr - jmp_addr - X64_JMP_SIZE;
+
+ jmp_buffer[0] = X64_JMP_OPCODE;
+ std::memcpy(&jmp_buffer[1], &jmp_offset, sizeof(uint32_t));
+
+ for (size_t i = 0; i < X64_JMP_SIZE; i++)
+ LLDB_LOGV(log, "0x{:x}", jmp_buffer[i]);
+
+ written_bytes =
+ process_sp->WriteMemory(jmp_addr, jmp_buffer, X64_JMP_SIZE, error);
+
+ if (written_bytes != X64_JMP_SIZE || error.Fail()) {
+ LLDB_LOG(log, "JIT: Couldn't override instruction with branching");
+ return false;
+ }
+
+ jmp_addr = trampoline_addr;
+
+ return true;
+}
+
size_t ABISysV_x86_64::GetRedZoneSize() const { return 128; }
// Static Functions
Index: lldb/source/Expression/LLVMUserExpression.cpp
===================================================================
--- lldb/source/Expression/LLVMUserExpression.cpp
+++ lldb/source/Expression/LLVMUserExpression.cpp
@@ -77,7 +77,7 @@
lldb::addr_t struct_address = LLDB_INVALID_ADDRESS;
if (!PrepareToExecuteJITExpression(diagnostic_manager, exe_ctx,
- struct_address)) {
+ struct_address, options)) {
diagnostic_manager.Printf(
eDiagnosticSeverityError,
"errored out in %s, couldn't PrepareToExecuteJITExpression",
@@ -88,7 +88,7 @@
lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS;
lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS;
- if (m_can_interpret) {
+ if (m_can_interpret && !options.GetInjectCondition()) {
llvm::Module *module = m_execution_unit_sp->GetModule();
llvm::Function *function = m_execution_unit_sp->GetFunction();
@@ -285,7 +285,7 @@
bool LLVMUserExpression::PrepareToExecuteJITExpression(
DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
- lldb::addr_t &struct_address) {
+ lldb::addr_t &struct_address, const EvaluateExpressionOptions options) {
lldb::TargetSP target;
lldb::ProcessSP process;
lldb::StackFrameSP frame;
@@ -302,8 +302,9 @@
Status alloc_error;
IRMemoryMap::AllocationPolicy policy =
- m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly
- : IRMemoryMap::eAllocationPolicyMirror;
+ m_can_interpret && !options.GetInjectCondition()
+ ? IRMemoryMap::eAllocationPolicyHostOnly
+ : IRMemoryMap::eAllocationPolicyMirror;
const bool zero_memory = false;
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -87,6 +87,9 @@
Arg<"Command">,
Desc<"A command to run when the breakpoint is hit, can be provided more "
"than once, the commands will get run in order left to right.">;
+ def breakpoint_modify_inject_condition : Option<"inject-condition", "I">,
+ Desc<"The breakpoint injects the condition expression into the process "
+ "machine code. Enables Fast Conditional Breakpoints.">;
}
let Command = "breakpoint dummy" in {
Index: lldb/source/Commands/CommandObjectBreakpoint.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -102,6 +102,13 @@
m_bp_opts.SetIgnoreCount(ignore_count);
}
break;
+ case 'I': {
+ if (!m_bp_opts.IsOptionSet(BreakpointOptions::eCondition))
+ error.SetErrorString("inject-condition option only available for "
+ "conditional breakpoints");
+ else
+ m_bp_opts.SetInjectCondition(true);
+ } break;
case 'o': {
bool value, success;
value = OptionArgParser::ToBoolean(option_arg, false, &success);
Index: lldb/source/Breakpoint/CMakeLists.txt
===================================================================
--- lldb/source/Breakpoint/CMakeLists.txt
+++ lldb/source/Breakpoint/CMakeLists.txt
@@ -2,6 +2,7 @@
Breakpoint.cpp
BreakpointID.cpp
BreakpointIDList.cpp
+ BreakpointInjectedSite.cpp
BreakpointList.cpp
BreakpointLocation.cpp
BreakpointLocationCollection.cpp
Index: lldb/source/Breakpoint/BreakpointSite.cpp
===================================================================
--- lldb/source/Breakpoint/BreakpointSite.cpp
+++ lldb/source/Breakpoint/BreakpointSite.cpp
@@ -20,14 +20,15 @@
BreakpointSite::BreakpointSite(BreakpointSiteList *list,
const BreakpointLocationSP &owner,
- lldb::addr_t addr, bool use_hardware)
+ lldb::addr_t addr, bool use_hardware,
+ BreakpointSiteKind kind)
: StoppointLocation(GetNextID(), addr, 0, use_hardware),
m_type(eSoftware), // Process subclasses need to set this correctly using
// SetType()
m_saved_opcode(), m_trap_opcode(),
m_enabled(false), // Need to create it disabled, so the first enable turns
// it on.
- m_owners(), m_owners_mutex() {
+ m_owners(), m_owners_mutex(), m_kind(kind) {
m_owners.Add(owner);
}
Index: lldb/source/Breakpoint/BreakpointOptions.cpp
===================================================================
--- lldb/source/Breakpoint/BreakpointOptions.cpp
+++ lldb/source/Breakpoint/BreakpointOptions.cpp
@@ -109,8 +109,8 @@
const char *BreakpointOptions::g_option_names[(
size_t)BreakpointOptions::OptionNames::LastOptionName]{
- "ConditionText", "IgnoreCount",
- "EnabledState", "OneShotState", "AutoContinue"};
+ "ConditionText", "IgnoreCount", "EnabledState",
+ "OneShotState", "AutoContinue", "JITCondition"};
bool BreakpointOptions::NullCallback(void *baton,
StoppointCallbackContext *context,
@@ -124,25 +124,23 @@
: m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
m_baton_is_command_baton(false), m_callback_is_synchronous(false),
m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_up(),
- m_condition_text(), m_condition_text_hash(0), m_auto_continue(false),
- m_set_flags(0) {
+ m_condition_text(), m_condition_text_hash(0), m_inject_condition(false),
+ m_auto_continue(false), m_set_flags(0) {
if (all_flags_set)
m_set_flags.Set(~((Flags::ValueType)0));
}
BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
- int32_t ignore, bool one_shot,
- bool auto_continue)
+ int32_t ignore, bool one_shot,
+ bool auto_continue, bool inject_condition)
: m_callback(nullptr), m_baton_is_command_baton(false),
m_callback_is_synchronous(false), m_enabled(enabled),
- m_one_shot(one_shot), m_ignore_count(ignore),
- m_condition_text_hash(0), m_auto_continue(auto_continue)
-{
- m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
- | eAutoContinue);
- if (condition && *condition != '\0') {
- SetCondition(condition);
- }
+ m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0),
+ m_inject_condition(inject_condition), m_auto_continue(auto_continue) {
+ m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);
+ if (condition && *condition != '\0') {
+ SetCondition(condition);
+ }
}
// BreakpointOptions copy constructor
@@ -152,6 +150,7 @@
m_callback_is_synchronous(rhs.m_callback_is_synchronous),
m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
m_ignore_count(rhs.m_ignore_count), m_thread_spec_up(),
+ m_inject_condition(rhs.m_inject_condition),
m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {
if (rhs.m_thread_spec_up != nullptr)
m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
@@ -175,6 +174,7 @@
m_condition_text_hash = rhs.m_condition_text_hash;
m_auto_continue = rhs.m_auto_continue;
m_set_flags = rhs.m_set_flags;
+ m_inject_condition = rhs.m_inject_condition;
return *this;
}
@@ -210,12 +210,18 @@
m_condition_text.clear();
m_condition_text_hash = 0;
m_set_flags.Clear(eCondition);
+ m_inject_condition = false;
} else {
m_condition_text = incoming.m_condition_text;
m_condition_text_hash = incoming.m_condition_text_hash;
m_set_flags.Set(eCondition);
+ m_inject_condition = incoming.m_inject_condition;
}
}
+ if (incoming.m_set_flags.Test(eInjectCondition)) {
+ m_inject_condition = incoming.m_inject_condition;
+ m_set_flags.Set(eInjectCondition);
+ }
if (incoming.m_set_flags.Test(eAutoContinue))
{
m_auto_continue = incoming.m_auto_continue;
@@ -374,10 +380,14 @@
if (m_set_flags.Test(eIgnoreCount))
options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
m_ignore_count);
- if (m_set_flags.Test(eCondition))
+ if (m_set_flags.Test(eCondition)) {
options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
m_condition_text);
-
+ if (m_set_flags.Test(eInjectCondition))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::InjectCondition),
+ m_inject_condition);
+ }
+
if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
auto cmd_baton =
std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
@@ -464,6 +474,10 @@
return m_callback != BreakpointOptions::NullCallback;
}
+bool BreakpointOptions::GetInjectCondition() const {
+ return m_inject_condition;
+}
+
bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
if (!HasCallback())
return false;
@@ -482,6 +496,7 @@
if (!condition || condition[0] == '\0') {
condition = "";
m_set_flags.Clear(eCondition);
+ m_set_flags.Clear(eInjectCondition);
}
else
m_set_flags.Set(eCondition);
@@ -571,8 +586,9 @@
}
if (!m_condition_text.empty()) {
if (level != eDescriptionLevelBrief) {
+ std::string fast = (m_inject_condition) ? " (FAST)" : "";
s->EOL();
- s->Printf("Condition: %s\n", m_condition_text.c_str());
+ s->Printf("Condition: %s%s\n", m_condition_text.c_str(), fast.c_str());
}
}
}
@@ -671,4 +687,5 @@
m_callback_is_synchronous = false;
m_enabled = false;
m_condition_text.clear();
+ m_inject_condition = false;
}
Index: lldb/source/Breakpoint/BreakpointLocation.cpp
===================================================================
--- lldb/source/Breakpoint/BreakpointLocation.cpp
+++ lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -226,6 +226,19 @@
->GetConditionText(hash);
}
+bool BreakpointLocation::GetInjectCondition() const {
+ if (m_options_up &&
+ m_options_up->IsOptionSet(BreakpointOptions::eInjectCondition))
+ return m_options_up->GetInjectCondition();
+ return m_owner.GetInjectCondition();
+}
+
+void BreakpointLocation::SetInjectCondition(bool inject_condition) {
+ m_owner.SetInjectCondition(inject_condition);
+ GetLocationOptions()->SetInjectCondition(inject_condition);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeInjectedCondition);
+}
+
bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
Status &error) {
Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
@@ -240,6 +253,14 @@
return false;
}
+ bool inject_condition = GetInjectCondition();
+
+ if (inject_condition) {
+ // TODO: Evalutates condition is case of multi-condition
+ // BreakpointInjectSite
+ return true;
+ }
+
error.Clear();
DiagnosticManager diagnostics;
@@ -263,9 +284,12 @@
return true;
}
- if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,
- eExecutionPolicyOnlyWhenNeeded, true,
- false)) {
+ ExecutionPolicy execution_policy = inject_condition
+ ? eExecutionPolicyAlways
+ : eExecutionPolicyOnlyWhenNeeded;
+
+ if (!m_user_expression_sp->Parse(diagnostics, exe_ctx, execution_policy,
+ true, false)) {
error.SetErrorStringWithFormat(
"Couldn't parse conditional expression:\n%s",
diagnostics.GetString().c_str());
@@ -287,6 +311,7 @@
options.SetTryAllThreads(true);
options.SetResultIsInternal(
true); // Don't generate a user variable for condition expressions.
+ options.SetInjectCondition(inject_condition);
Status expr_error;
Index: lldb/source/Breakpoint/BreakpointInjectedSite.cpp
===================================================================
--- /dev/null
+++ lldb/source/Breakpoint/BreakpointInjectedSite.cpp
@@ -0,0 +1,449 @@
+//===-- BreakpointInjectedSite.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointInjectedSite.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangExpressionVariable.h"
+#include "Plugins/ExpressionParser/Clang/ClangUserExpression.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Target/Language.h"
+
+#include "lldb/Target/ABI.h"
+
+#include "llvm/Support/DataExtractor.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointInjectedSite::BreakpointInjectedSite(
+ BreakpointSiteList *list, const BreakpointLocationSP &owner,
+ lldb::addr_t addr)
+ : BreakpointSite(list, owner, addr, false, eKindBreakpointInjectedSite),
+ m_target_sp(owner->GetTarget().shared_from_this()),
+ m_real_addr(owner->GetAddress()), m_trap_addr(LLDB_INVALID_ADDRESS) {}
+
+BreakpointInjectedSite::~BreakpointInjectedSite() {}
+
+bool BreakpointInjectedSite::BuildConditionExpression(void) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_JIT_LOADER);
+
+ Status error;
+
+ std::string trap;
+ std::string condition_text;
+ bool single_condition = true;
+
+ LanguageType language = eLanguageTypeUnknown;
+
+ for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) {
+
+ // Stop building the expression if a location condition is not JIT-ed
+ if (!loc_sp->GetInjectCondition()) {
+ LLDB_LOG(log, "FCB: BreakpointLocation ({}) condition is not JIT-ed",
+ loc_sp->GetConditionText());
+ return false;
+ }
+
+ std::string condition = loc_sp->GetConditionText();
+ // See if we can figure out the language from the frame, otherwise use the
+ // default language:
+ CompileUnit *comp_unit =
+ loc_sp->GetAddress().CalculateSymbolContextCompileUnit();
+ if (comp_unit)
+ language = comp_unit->GetLanguage();
+
+ if (language == eLanguageTypeSwift) {
+ trap += "Builtin.int_trap()";
+ } else if (Language::LanguageIsCFamily(language)) {
+ trap = "__builtin_debugtrap()";
+ } else {
+ LLDB_LOG(log, "FCB: Language {} not supported",
+ Language::GetNameForLanguageType(language));
+ m_condition_expression_sp.reset();
+ return false;
+ }
+
+ condition_text += "static int hit_count = 0;\n\thit_count++;\n\t";
+ condition_text += (single_condition) ? "if (" : " || ";
+ condition_text += condition;
+
+ single_condition = false;
+ }
+
+ condition_text += ") {\n\t";
+
+ condition_text += trap + ";\n}";
+
+ LLDB_LOGV(log, "Injected Condition:\n{}\n", condition_text.c_str());
+
+ DiagnosticManager diagnostics;
+
+ EvaluateExpressionOptions options;
+ options.SetInjectCondition(true);
+ options.SetKeepInMemory(true);
+ options.SetGenerateDebugInfo(true);
+
+ m_condition_expression_sp.reset(m_target_sp->GetUserExpressionForLanguage(
+ condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,
+ EvaluateExpressionOptions(options), nullptr, error));
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf("Error getting condition expression: %s.", error.AsCString());
+ m_condition_expression_sp.reset();
+ return false;
+ }
+
+ diagnostics.Clear();
+
+ ThreadSP thread_sp = m_target_sp->GetProcessSP()
+ ->GetThreadList()
+ .GetExpressionExecutionThread();
+
+ user_id_t frame_idx = -1;
+ user_id_t concrete_frame_idx = -1;
+ addr_t cfa = LLDB_INVALID_ADDRESS;
+ bool cfa_is_valid = false;
+ addr_t pc = LLDB_INVALID_ADDRESS;
+ StackFrame::Kind frame_kind = StackFrame::Kind::Regular;
+ bool zeroth_frame = false;
+ SymbolContext sc;
+ m_real_addr.CalculateSymbolContext(&sc);
+
+ StackFrameSP frame_sp =
+ make_shared<StackFrame>(thread_sp, frame_idx, concrete_frame_idx, cfa,
+ cfa_is_valid, pc, frame_kind, zeroth_frame, &sc);
+
+ m_owner_exe_ctx = ExecutionContext(frame_sp);
+ ExecutionPolicy execution_policy = eExecutionPolicyAlways;
+ bool keep_result_in_memory = true;
+ bool generate_debug_info = true;
+
+ if (!m_condition_expression_sp->Parse(diagnostics, m_owner_exe_ctx,
+ execution_policy, keep_result_in_memory,
+ generate_debug_info)) {
+ LLDB_LOG(log, "Couldn't parse conditional expression:\n{}",
+ diagnostics.GetString().c_str());
+ m_condition_expression_sp.reset();
+ return false;
+ }
+
+ const AddressRange &jit_addr_range =
+ m_condition_expression_sp->GetJITAddressRange();
+
+ error.Clear();
+
+ void *buffer = std::calloc(jit_addr_range.GetByteSize(), sizeof(uint8_t));
+
+ lldb::addr_t jit_addr =
+ jit_addr_range.GetBaseAddress().GetCallableLoadAddress(m_target_sp.get());
+
+ size_t memory_read = m_target_sp->GetProcessSP()->ReadMemory(
+ jit_addr, buffer, jit_addr_range.GetByteSize(), error);
+
+ if (memory_read != jit_addr_range.GetByteSize() || error.Fail()) {
+ m_condition_expression_sp.reset();
+ error.SetErrorString("Couldn't read jit memory");
+ return false;
+ }
+
+ PlatformSP platform_sp = m_target_sp->GetPlatform();
+
+ if (!platform_sp) {
+ error.SetErrorString("Couldn't get running platform");
+ return false;
+ }
+
+ if (!platform_sp->GetSoftwareBreakpointTrapOpcode(*m_target_sp.get(), this)) {
+ error.SetErrorString("Couldn't get current architecture trap opcode");
+ return false;
+ }
+
+ if (!ResolveTrapAddress(buffer, memory_read)) {
+ error.SetErrorString("Couldn't find trap in jitter expression");
+ return false;
+ }
+
+ if (!GatherArgumentsMetadata()) {
+ LLDB_LOG(log, "FCB: Couldn't gather argument metadata");
+ return false;
+ }
+
+ if (!CreateArgumentsStructure()) {
+ LLDB_LOG(log, "FCB: Couldn't create argument structure");
+ return false;
+ }
+
+ return true;
+}
+
+bool BreakpointInjectedSite::ResolveTrapAddress(void *jit, size_t size) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_JIT_LOADER);
+
+ const ArchSpec &arch = m_target_sp->GetArchitecture();
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const ExecutionContext exe_ctx(m_target_sp, true);
+ const bool prefer_file_cache = true;
+
+ m_disassembler_sp = Disassembler::DisassembleRange(
+ arch, plugin_name, flavor, exe_ctx,
+ m_condition_expression_sp->GetJITAddressRange(), prefer_file_cache);
+
+ if (!m_disassembler_sp) {
+ LLDB_LOG(log, "FCB: Couldn't disassemble JIT-ed expression");
+ return false;
+ }
+
+ InstructionList &instructions = m_disassembler_sp->GetInstructionList();
+
+ if (!instructions.GetSize()) {
+ LLDB_LOG(log, "FCB: No instructions found for JIT-ed expression");
+ return false;
+ }
+
+ for (size_t i = 0; i < instructions.GetSize(); i++) {
+ InstructionSP instr = instructions.GetInstructionAtIndex(i);
+ const void *instr_opcode = instr->GetOpcode().GetOpcodeDataBytes();
+
+ DataExtractor data;
+ instr->GetData(data);
+
+ const size_t trap_size = instr->Decode(*m_disassembler_sp.get(), data, 0);
+
+ if (!instr_opcode) {
+ return false;
+ }
+
+ if (!memcmp(instr_opcode, m_trap_opcode, trap_size)) {
+ addr_t addr = instr->GetAddress().GetOpcodeLoadAddress(m_target_sp.get());
+ m_trap_addr = addr;
+ LLDB_LOGV(log, "Injected trap address: {0:X+}", addr);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BreakpointInjectedSite::GatherArgumentsMetadata() {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_JIT_LOADER);
+
+ LanguageType native_language = m_condition_expression_sp->Language();
+
+ if (!Language::LanguageIsCFamily(native_language)) {
+ LLDB_LOG(log, "FCB: {} language does not support Injected Conditional \
+ Breapoint",
+ Language::GetNameForLanguageType(native_language));
+ return false;
+ }
+
+ ClangUserExpression *clang_expr =
+ llvm::dyn_cast<ClangUserExpression>(m_condition_expression_sp.get());
+
+ ClangExpressionDeclMap *decl_map = clang_expr->DeclMap();
+ if (!decl_map) {
+ LLDB_LOG(log, "FCB: Couldn't find DeclMap for JIT-ed expression");
+ return false;
+ }
+
+ uint32_t num_elements;
+ size_t size;
+ offset_t alignment;
+
+ if (!decl_map->GetStructInfo(num_elements, size, alignment)) {
+ LLDB_LOG(log, "FCB: Couldn't fetch arguments info from DeclMap");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_elements; ++i) {
+ const clang::NamedDecl *decl = nullptr;
+ llvm::Value *value = nullptr;
+ lldb::offset_t offset;
+ lldb_private::ConstString name;
+
+ if (!decl_map->GetStructElement(decl, value, offset, name, i)) {
+ LLDB_LOG(log, "FCB: Couldn't fetch element from DeclMap");
+ return false;
+ }
+ if (!value) {
+ LLDB_LOG(log, "FCB: Couldn't find value for element {}/{}", i,
+ num_elements);
+ return false;
+ }
+ }
+
+ ExpressionVariableList &members = decl_map->GetStructMembers();
+
+ for (ExpressionVariableSP expr_var : members.Variables()) {
+ ValueObjectSP val_obj_sp = expr_var->GetValueObject();
+
+ if (!val_obj_sp->GetVariable()) {
+ // if Expression Variable does not have ValueObject, skip it
+ continue;
+ }
+
+ VariableSP var_sp = val_obj_sp->GetVariable();
+
+ DWARFExpression lldb_dwarf_expr = var_sp->LocationExpression();
+ DataExtractor lldb_data;
+ if (!lldb_dwarf_expr.GetExpressionData(lldb_data)) {
+ return false;
+ }
+
+ llvm::StringRef data(lldb_data.PeekCStr(0));
+ bool is_le = (lldb_data.GetByteOrder() == lldb::eByteOrderLittle);
+ uint32_t data_addr_size = lldb_data.GetAddressByteSize();
+ llvm::DataExtractor llvm_data =
+ llvm::DataExtractor(data, is_le, data_addr_size);
+
+ llvm::DWARFExpression::Operation::DwarfVersion version =
+ llvm::DWARFExpression::Operation::Dwarf5;
+ uint8_t addr_size = m_target_sp->GetArchitecture().GetAddressByteSize();
+
+ auto size = var_sp->GetType()->GetByteSize();
+ if (!size) {
+ LLDB_LOG(log, "FCB: Variable {} has invalid size",
+ var_sp->GetName().GetCString());
+ return false;
+ }
+
+ VariableMetadata metadata(expr_var->GetName().GetStringRef(),
+ size.getValue(), llvm_data, version, addr_size);
+
+ m_metadatas.push_back(metadata);
+ }
+
+ clang_expr->ResetDeclMap();
+
+ return true;
+}
+
+bool BreakpointInjectedSite::CreateArgumentsStructure() {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_JIT_LOADER);
+
+ Status error;
+ std::string expr;
+ expr.reserve(2048);
+ std::string name = "$__lldb_create_args_struct";
+
+ ABISP abi_sp = m_owner_exe_ctx.GetProcessSP()->GetABI();
+
+ expr += "extern unsigned int mach_task_self_;\n";
+
+ expr += "extern \"C\"\n"
+ "{\n"
+ " /*\n"
+ " * mach defines\n"
+ " */\n"
+ "\n";
+
+ expr += abi_sp->GetMachTypesAsString();
+
+ expr += "\n"
+ " void* memcpy (void *dest, const void *src, size_t count);\n"
+ " kern_return_t mach_vm_allocate (vm_map_t target, "
+ "mach_vm_address_t *address, mach_vm_size_t size, int flags);\n"
+ " kern_return_t mach_vm_deallocate (vm_map_t target, "
+ "mach_vm_address_t address, mach_vm_size_t size);\n"
+ "}\n\n";
+
+ expr += "#define KERN_SUCCESS 0\n"
+ "#define KERN_INVALID_ADDRESS 1\n\n";
+
+ // Get structure size
+ std::size_t size = m_metadatas.size() * 8;
+ expr += "static mach_vm_size_t size = " + std::to_string(size) + ";\n\n";
+
+ expr += abi_sp->GetRegisterContextAsString();
+
+ expr +=
+ "mach_vm_address_t $__lldb_create_args_struct(register_context* regs) {\n"
+ " mach_vm_address_t address = (vm_address_t) NULL;\n"
+ " int flags = 1;\n"
+ " \n"
+ " kern_return_t kr = mach_vm_allocate(mach_task_self_, &address, "
+ "size, flags);\n"
+ " if (kr != KERN_SUCCESS) {\n"
+ " return KERN_INVALID_ADDRESS;\n"
+ " }\n\n"
+ " void *src_addr = NULL;\n"
+ " void *dst_addr = NULL;\n"
+ " size_t count = sizeof(void*);\n\n"
+ "\n";
+
+ for (size_t index = 0; index < m_metadatas.size(); index++) {
+ expr += ParseDWARFExpression(index, error);
+ if (error.Fail()) {
+ LLDB_LOG(log, "FCB: Couldn't parse DWARFExpression ({}/{})", index,
+ m_metadatas.size());
+ return false;
+ }
+ }
+
+ expr += " return address;\n"
+ "}\n";
+
+ m_create_args_struct_function_sp.reset(
+ m_target_sp->GetUtilityFunctionForLanguage(expr.c_str(), eLanguageTypeC,
+ name.c_str(), error));
+
+ if (error.Fail()) {
+ LLDB_LOG(log, "Error getting utility function: {1}.", error);
+ m_create_args_struct_function_sp.reset();
+ return false;
+ }
+
+ DiagnosticManager diagnostics;
+ ExecutionContext exe_ctx(m_target_sp->GetProcessSP());
+
+ if (!m_create_args_struct_function_sp->Install(diagnostics, exe_ctx)) {
+ error.SetErrorStringWithFormat("Couldn't install utility function:\n%s",
+ diagnostics.GetString().c_str());
+ m_create_args_struct_function_sp.reset();
+ return false;
+ }
+
+ return true;
+}
+
+std::string BreakpointInjectedSite::ParseDWARFExpression(size_t index,
+ Status &error) {
+ std::string expr;
+
+ for (auto op : m_metadatas[index].dwarf) {
+ switch (op.getCode()) {
+ case DW_OP_addr: {
+ int64_t operand = op.getRawOperand(0);
+ expr += " src_addr = " + std::to_string(operand) +
+ ";\n"
+ " dst_addr = (void*) (address + " +
+ std::to_string(index * 8) +
+ ");\n"
+ " memcpy(dst_addr, &src_addr, count);\n";
+ break;
+ }
+ case DW_OP_fbreg: {
+ int64_t operand = op.getRawOperand(0);
+ expr += " src_addr = (void*) (regs->rbp + " + std::to_string(operand) +
+ ");\n"
+ " dst_addr = (void*) (address + " +
+ std::to_string(index * 8) +
+ ");\n"
+ " memcpy(dst_addr, &src_addr, count);\n";
+ break;
+ }
+ default: {
+ error.Clear();
+ error.SetErrorToErrno();
+ break;
+ }
+ }
+ }
+
+ return expr;
+}
Index: lldb/source/Breakpoint/Breakpoint.cpp
===================================================================
--- lldb/source/Breakpoint/Breakpoint.cpp
+++ lldb/source/Breakpoint/Breakpoint.cpp
@@ -418,6 +418,14 @@
return m_options_up->GetConditionText();
}
+void Breakpoint::SetInjectCondition(bool inject_condition) {
+ m_options_up->SetInjectCondition(inject_condition);
+}
+
+bool Breakpoint::GetInjectCondition() const {
+ return m_options_up->GetInjectCondition();
+}
+
// This function is used when "baton" doesn't need to be freed
void Breakpoint::SetCallback(BreakpointHitCallback callback, void *baton,
bool is_synchronous) {
Index: lldb/source/API/SBBreakpointLocation.cpp
===================================================================
--- lldb/source/API/SBBreakpointLocation.cpp
+++ lldb/source/API/SBBreakpointLocation.cpp
@@ -206,6 +206,32 @@
return false;
}
+void SBBreakpointLocation::SetInjectCondition(bool inject_condition) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetInjectCondition, (bool),
+ inject_condition);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (!loc_sp)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetInjectCondition(inject_condition);
+ loc_sp->GetBreakpoint().SetInjectCondition(inject_condition);
+}
+
+bool SBBreakpointLocation::GetInjectCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointLocation, GetInjectCondition);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (!loc_sp)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetInjectCondition();
+}
+
void SBBreakpointLocation::SetScriptCallbackFunction(
const char *callback_function_name) {
LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
@@ -480,6 +506,8 @@
LLDB_REGISTER_METHOD(const char *, SBBreakpointLocation, GetCondition, ());
LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetAutoContinue, (bool));
LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetAutoContinue, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetInjectCondition, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetInjectCondition, ());
LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
(const char *));
LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointLocation,
Index: lldb/source/API/SBBreakpoint.cpp
===================================================================
--- lldb/source/API/SBBreakpoint.cpp
+++ lldb/source/API/SBBreakpoint.cpp
@@ -312,6 +312,31 @@
return false;
}
+void SBBreakpoint::SetInjectCondition(bool inject_condition) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetInjectCondition, (bool),
+ inject_condition);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetInjectCondition(inject_condition);
+}
+
+bool SBBreakpoint::GetInjectCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpoint, GetInjectCondition);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->GetInjectCondition();
+}
+
uint32_t SBBreakpoint::GetHitCount() const {
LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpoint, GetHitCount);
@@ -967,6 +992,8 @@
LLDB_REGISTER_METHOD(const char *, SBBreakpoint, GetCondition, ());
LLDB_REGISTER_METHOD(void, SBBreakpoint, SetAutoContinue, (bool));
LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetAutoContinue, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetInjectCondition, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetInjectCondition, ());
LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpoint, GetHitCount, ());
LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpoint, GetIgnoreCount, ());
LLDB_REGISTER_METHOD(void, SBBreakpoint, SetThreadID, (lldb::tid_t));
Index: lldb/scripts/interface/SBBreakpointLocation.i
===================================================================
--- lldb/scripts/interface/SBBreakpointLocation.i
+++ lldb/scripts/interface/SBBreakpointLocation.i
@@ -72,6 +72,15 @@
void SetAutoContinue(bool auto_continue);
+ %feature("docstring", "
+ *EXPERIMENTAL* Check if the condition expression is injected and checked in-process.") GetInjectCondition;
+ bool GetInjectCondition();
+
+ %feature("docstring", "
+ *EXPERIMENTAL* The condition expression in injected and checked in-process.
+ Enables Fast Conditional Breakpoint.") SetInjectCondition;
+ void SetInjectCondition(bool inject_condition);
+
%feature("docstring", "
Set the callback to the given Python function name.") SetScriptCallbackFunction;
void
Index: lldb/scripts/interface/SBBreakpoint.i
===================================================================
--- lldb/scripts/interface/SBBreakpoint.i
+++ lldb/scripts/interface/SBBreakpoint.i
@@ -150,6 +150,15 @@
bool GetAutoContinue();
+ %feature("docstring", "
+ *EXPERIMENTAL* Check if the condition expression is injected and checked in-process.") GetInjectCondition;
+ bool GetInjectCondition();
+
+ %feature("docstring", "
+ *EXPERIMENTAL* The condition expression in injected and checked in-process.
+ Toggles Fast Conditional Breakpoint.") SetInjectCondition;
+ void SetInjectCondition(bool inject_condition);
+
void
SetThreadID (lldb::tid_t sb_thread_id);
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/main.c
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/main.c
@@ -0,0 +1,24 @@
+// This simple program is to demonstrate the capability of the lldb command
+// "breakpoint set -c "condition() == 999999" -f main.c -l 29 -I" or
+// "breakpoint set -c "local_count == 999999" -f main.c -l 29 -I" to break
+// the condition for an inject breakpoint evaluate to true.
+
+#include <stdio.h>
+
+static int global_count = 0;
+
+int condition(void) {
+ printf("global_count = %d\n", global_count);
+ return global_count++;
+}
+
+int main(int argc, char *argv[])
+{
+ int local_count = 0;
+ for (int i = 0; i < 10000000; i++) {
+ printf("local_count = %d\n", local_count++); // Find the line number of condition breakpoint for local_count
+ }
+
+ return 0;
+}
+
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/TestFastConditionalBreakpoints.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/TestFastConditionalBreakpoints.py
@@ -0,0 +1,191 @@
+"""
+Test Fast Conditional Breakpoints.
+"""
+
+from __future__ import print_function
+
+import os
+import time
+import re
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class FastConditionalBreakpoitsTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ self.file = lldb.SBFileSpec("main.c")
+ self.comment = "Find the line number of condition breakpoint for local_count"
+ self.condition = '"local_count == 9"'
+ self.extra_options = "-c " + self.condition + " -I"
+ self.binary = "a.out"
+
+ @skipIfWindows
+ def test_fast_conditional_breakpoint_flag_interpreter(self):
+ """Enable fast conditional breakpoints with 'breakpoint modify -c <expr> id -I'."""
+ self.build()
+ self.enable_fast_conditional_breakpoint(use_interpreter=True)
+
+ @skipIfWindows
+ @add_test_categories(["pyapi"])
+ def test_fast_conditional_breakpoint_flag_api(self):
+ """Exercise fast conditional breakpoints with SB API"""
+ self.build()
+ self.enable_fast_conditional_breakpoint(use_interpreter=False)
+
+ @skipIfWindows
+ @add_test_categories(["pyapi"])
+ def test_fast_conditional_breakpoint(self):
+ """Exercice injected breakpoint conditions"""
+ self.build()
+ self.inject_fast_conditional_breakpoint()
+
+ @skipIfWindows
+ @add_test_categories(["pyapi"])
+ def test_invalid_fast_conditional_breakpoint(self):
+ """Exercice invalid injected breakpoint conditions"""
+ self.build()
+ self.inject_invalid_fast_conditional_breakpoint()
+
+ def enable_fast_conditional_breakpoint(self, use_interpreter):
+ exe = self.getBuildArtifact(self.binary)
+ self.target = self.dbg.CreateTarget(exe)
+ self.assertTrue(self.target, VALID_TARGET)
+
+ if use_interpreter:
+ lldbutil.run_break_set_by_source_regexp(
+ self, self.comment, self.extra_options
+ )
+
+ self.runCmd("breakpoint modify " + self.condition + " 1")
+
+ self.expect("breakpoint list -f", substrs=["(FAST)"])
+ else:
+ # Now create a breakpoint on main.c by source regex'.
+ breakpoint = self.target.BreakpointCreateBySourceRegex(
+ self.comment, self.file
+ )
+ self.assertTrue(
+ breakpoint and breakpoint.GetNumLocations() == 1,
+ VALID_BREAKPOINT)
+
+ # We didn't associate a thread index with the breakpoint, so it should
+ # be invalid.
+ self.assertTrue(
+ breakpoint.GetThreadIndex() == lldb.UINT32_MAX,
+ "the thread index should be invalid",
+ )
+ # The thread name should be invalid, too.
+ self.assertTrue(
+ breakpoint.GetThreadName() is None,
+ "the thread name should be invalid")
+
+ # Let's set the thread index for this breakpoint and verify that it is,
+ # indeed, being set correctly and there's only one thread for the
+ # process.
+ breakpoint.SetThreadIndex(1)
+ self.assertTrue(
+ breakpoint.GetThreadIndex() == 1,
+ "the thread index has been set correctly",
+ )
+
+ # Get the breakpoint location from breakpoint after we verified that,
+ # indeed, it has one location.
+ location = breakpoint.GetLocationAtIndex(0)
+ self.assertTrue(
+ location and location.IsEnabled(), VALID_BREAKPOINT_LOCATION
+ )
+
+ # Set the condition on the breakpoint.
+ location.SetCondition(self.condition)
+ self.expect(
+ location.GetCondition(),
+ exe=False,
+ startstr=self.condition)
+
+ # Set condition on the breakpoint to be injected in-process.
+ location.SetInjectCondition(True)
+ self.assertTrue(
+ location.GetInjectCondition(),
+ VALID_BREAKPOINT_LOCATION)
+
+ return self.target.GetBreakpointAtIndex(0)
+
+ def inject_fast_conditional_breakpoint(self):
+ # now launch the process, and do not stop at entry point.
+ breakpoint = self.enable_fast_conditional_breakpoint(
+ use_interpreter=False)
+ process = self.target.LaunchSimple(
+ None, None, self.get_process_working_directory()
+ )
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ # frame #0 should be on self.line and the break condition should hold.
+ from lldbsuite.test.lldbutil import get_stopped_thread
+
+ thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+ self.assertTrue(
+ thread and thread.IsValid(),
+ "there should be a thread stopped due to breakpoint condition",
+ )
+
+ frame0 = thread.GetFrameAtIndex(0)
+ expected_fn_name = "$__lldb_expr(void*)"
+ self.assertTrue(frame0 and frame0.GetFunctionName()
+ == expected_fn_name)
+
+ # the hit count for the breakpoint should be 1.
+ self.assertTrue(breakpoint.GetHitCount() == 1)
+
+ def inject_invalid_fast_conditional_breakpoint(self):
+ # now create a breakpoint on main.c by source regex'.
+ exe = self.getBuildArtifact(self.binary)
+ self.target = self.dbg.CreateTarget(exe)
+ self.assertTrue(self.target, VALID_TARGET)
+ breakpoint = self.target.BreakpointCreateBySourceRegex(
+ self.comment, self.file)
+ self.assertTrue(
+ breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
+ )
+
+ # set the condition on the breakpoint.
+ breakpoint.SetCondition("no_such_variable == not_this_one_either")
+ self.expect(
+ breakpoint.GetCondition(),
+ exe=False,
+ startstr="no_such_variable == not_this_one_either",
+ )
+ # get the breakpoint location from breakpoint after we verified that,
+ # indeed, it has one location.
+ location = breakpoint.GetLocationAtIndex(0)
+ self.assertTrue(
+ location and location.IsEnabled(),
+ VALID_BREAKPOINT_LOCATION)
+
+ # set condition on the breakpoint to be injected.
+ location.SetInjectCondition(True)
+ self.assertTrue(
+ location.GetInjectCondition(),
+ VALID_BREAKPOINT_LOCATION)
+
+ # now launch the process, and do not stop at entry point.
+ process = self.target.LaunchSimple(
+ None, None, self.get_process_working_directory()
+ )
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ # frame #0 should be on self.line1 and the break condition should hold.
+ from lldbsuite.test.lldbutil import get_stopped_thread
+
+ # FCB is disabled because the condition is not valid.
+ self.assertFalse(
+ location.GetInjectCondition(),
+ VALID_BREAKPOINT_LOCATION)
+ # FCB falls back to regular conditional breakpoint that get hit once.
+ self.assertTrue(breakpoint.GetHitCount() == 1)
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/Makefile
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/fast_conditional_breakpoints/Makefile
@@ -0,0 +1,6 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+CFLAGS_EXTRAS += -std=c99
+
+include $(LEVEL)/Makefile.rules
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -15,30 +15,22 @@
mydir = TestBase.compute_mydir(__file__)
- # Requires EE to support COFF on Windows (http://llvm.org/pr22232)
- @skipIfWindows
def test_breakpoint_condition_and_run_command(self):
"""Exercise breakpoint condition with 'breakpoint modify -c <expr> id'."""
self.build()
self.breakpoint_conditions()
- # Requires EE to support COFF on Windows (http://llvm.org/pr22232)
- @skipIfWindows
def test_breakpoint_condition_inline_and_run_command(self):
"""Exercise breakpoint condition inline with 'breakpoint set'."""
self.build()
self.breakpoint_conditions(inline=True)
- # Requires EE to support COFF on Windows (http://llvm.org/pr22232)
- @skipIfWindows
@add_test_categories(['pyapi'])
def test_breakpoint_condition_and_python_api(self):
"""Use Python APIs to set breakpoint conditions."""
self.build()
self.breakpoint_conditions_python()
- # Requires EE to support COFF on Windows (http://llvm.org/pr22232)
- @skipIfWindows
@add_test_categories(['pyapi'])
def test_breakpoint_invalid_condition_and_python_api(self):
"""Use Python APIs to set breakpoint conditions."""
Index: lldb/include/lldb/lldb-forward.h
===================================================================
--- lldb/include/lldb/lldb-forward.h
+++ lldb/include/lldb/lldb-forward.h
@@ -40,6 +40,7 @@
class BreakpointPrecondition;
class BreakpointResolver;
class BreakpointSite;
+class BreakpointInjectedSite;
class BreakpointSiteList;
class BroadcastEventSpec;
class Broadcaster;
@@ -297,6 +298,10 @@
typedef std::weak_ptr<lldb_private::Breakpoint> BreakpointWP;
typedef std::shared_ptr<lldb_private::BreakpointSite> BreakpointSiteSP;
typedef std::weak_ptr<lldb_private::BreakpointSite> BreakpointSiteWP;
+typedef std::shared_ptr<lldb_private::BreakpointInjectedSite>
+ BreakpointInjectedSiteSP;
+typedef std::weak_ptr<lldb_private::BreakpointInjectedSite>
+ BreakpointInjectedSiteWP;
typedef std::shared_ptr<lldb_private::BreakpointLocation> BreakpointLocationSP;
typedef std::weak_ptr<lldb_private::BreakpointLocation> BreakpointLocationWP;
typedef std::shared_ptr<lldb_private::BreakpointPrecondition> BreakpointPreconditionSP;
Index: lldb/include/lldb/lldb-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-enumerations.h
+++ lldb/include/lldb/lldb-enumerations.h
@@ -402,7 +402,8 @@
eBreakpointEventTypeConditionChanged = (1u << 9),
eBreakpointEventTypeIgnoreChanged = (1u << 10),
eBreakpointEventTypeThreadChanged = (1u << 11),
- eBreakpointEventTypeAutoContinueChanged = (1u << 12)};
+ eBreakpointEventTypeAutoContinueChanged = (1u << 12),
+ eBreakpointEventTypeInjectedCondition = (1u << 13)};
FLAGS_ENUM(WatchpointEventType){
eWatchpointEventTypeInvalidType = (1u << 0),
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -290,6 +290,12 @@
m_ignore_breakpoints = ignore;
}
+ void SetInjectCondition(bool inject_condition) {
+ m_inject_condition = inject_condition;
+ }
+
+ bool GetInjectCondition() const { return m_inject_condition; }
+
bool DoesKeepInMemory() const { return m_keep_in_memory; }
void SetKeepInMemory(bool keep = true) { m_keep_in_memory = keep; }
@@ -403,6 +409,7 @@
bool m_ansi_color_errors = false;
bool m_result_is_internal = false;
bool m_auto_apply_fixits = true;
+ bool m_inject_condition = false;
/// True if the executed code should be treated as utility code that is only
/// used by LLDB internally.
bool m_running_utility_expression = false;
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -21,8 +21,10 @@
#include <unordered_set>
#include <vector>
+#include "lldb/Breakpoint/BreakpointInjectedSite.h"
#include "lldb/Breakpoint/BreakpointSiteList.h"
#include "lldb/Core/Communication.h"
+#include "lldb/Core/Disassembler.h"
#include "lldb/Core/LoadedModuleInfoList.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/ThreadSafeValue.h"
@@ -2030,6 +2032,13 @@
lldb::break_id_t CreateBreakpointSite(const lldb::BreakpointLocationSP &owner,
bool use_hardware);
+ lldb::break_id_t
+ FallbackToRegularBreakpointSite(const lldb::BreakpointLocationSP &owner,
+ bool use_hardware, Log *log,
+ const char *error);
+
+ size_t SaveInstructions(Address &address);
+
Status DisableBreakpointSiteByID(lldb::user_id_t break_id);
Status EnableBreakpointSiteByID(lldb::user_id_t break_id);
@@ -2743,6 +2752,8 @@
std::unique_ptr<UtilityFunction> m_dlopen_utility_func_up;
std::once_flag m_dlopen_utility_func_flag_once;
+ void *m_overriden_instructions = nullptr;
+
size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
uint8_t *buf) const;
Index: lldb/include/lldb/Target/ExecutionContextScope.h
===================================================================
--- lldb/include/lldb/Target/ExecutionContextScope.h
+++ lldb/include/lldb/Target/ExecutionContextScope.h
@@ -13,7 +13,7 @@
namespace lldb_private {
-/// @class ExecutionContextScope ExecutionContextScope.h
+/// \class ExecutionContextScope ExecutionContextScope.h
/// "lldb/Target/ExecutionContextScope.h" Inherit from this if your object can
/// reconstruct its
/// execution context.
Index: lldb/include/lldb/Target/ABI.h
===================================================================
--- lldb/include/lldb/Target/ABI.h
+++ lldb/include/lldb/Target/ABI.h
@@ -133,6 +133,47 @@
virtual bool GetPointerReturnRegister(const char *&name) { return false; }
+ /// Allocate a memory stub for the fast condition breakpoint trampoline, and
+ /// build it by saving the register context, calling the argument structure
+ /// builder, passing the resulting structure to the condition checker,
+ /// restoring the register context, running the copied instructions and]
+ /// jumping back to the user source code.
+ ///
+ /// \param[in] instrs_size
+ /// The size in bytes of the copied instructions.
+ ///
+ /// \param[in] data
+ /// The copied instructions buffer.
+ ///
+ /// \param[in] jmp_addr
+ /// The address of the source .
+ ///
+ /// \param[in] util_func_addr
+ /// The address of the JIT-ed argument structure builder.
+ ///
+ /// \param[in] cond_expr_addr
+ /// The address of the JIT-ed condition checker.
+ ///
+ virtual bool SetupFastConditionalBreakpointTrampoline(
+ size_t instrs_size, void *data, lldb::addr_t &jmp_addr,
+ lldb::addr_t util_func_addr, lldb::addr_t cond_expr_addr) {
+ return false;
+ }
+
+ virtual llvm::ArrayRef<uint8_t> GetJumpOpcode() { return 0; }
+
+ virtual size_t GetJumpSize() { return 0; }
+
+ virtual llvm::ArrayRef<uint8_t> GetCallOpcode() { return 0; }
+
+ virtual size_t GetCallSize() { return 0; }
+
+ virtual llvm::StringRef GetRegisterContextAsString() { return ""; }
+
+ virtual llvm::StringRef GetMachTypesAsString() { return ""; }
+
+ virtual bool ImplementsJIT() { return false; }
+
static lldb::ABISP FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch);
protected:
Index: lldb/include/lldb/Symbol/VariableList.h
===================================================================
--- lldb/include/lldb/Symbol/VariableList.h
+++ lldb/include/lldb/Symbol/VariableList.h
@@ -11,6 +11,7 @@
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Variable.h"
+#include "lldb/Utility/Iterable.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
@@ -75,6 +76,13 @@
private:
// For VariableList only
DISALLOW_COPY_AND_ASSIGN(VariableList);
+
+public:
+ typedef AdaptedIterable<collection, lldb::VariableSP, vector_adapter>
+ VariableListCollectionIterable;
+ VariableListCollectionIterable Variables() {
+ return VariableListCollectionIterable(m_variables);
+ }
};
} // namespace lldb_private
Index: lldb/include/lldb/Expression/LLVMUserExpression.h
===================================================================
--- lldb/include/lldb/Expression/LLVMUserExpression.h
+++ lldb/include/lldb/Expression/LLVMUserExpression.h
@@ -81,7 +81,8 @@
bool PrepareToExecuteJITExpression(DiagnosticManager &diagnostic_manager,
ExecutionContext &exe_ctx,
- lldb::addr_t &struct_address);
+ lldb::addr_t &struct_address,
+ const EvaluateExpressionOptions options);
virtual bool AddArguments(ExecutionContext &exe_ctx,
std::vector<lldb::addr_t> &args,
Index: lldb/include/lldb/Expression/ExpressionVariable.h
===================================================================
--- lldb/include/lldb/Expression/ExpressionVariable.h
+++ lldb/include/lldb/Expression/ExpressionVariable.h
@@ -16,6 +16,7 @@
#include "lldb/Core/ValueObject.h"
#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Iterable.h"
#include "lldb/lldb-public.h"
namespace lldb_private {
@@ -199,7 +200,19 @@
void Clear() { m_variables.clear(); }
private:
- std::vector<lldb::ExpressionVariableSP> m_variables;
+ typedef std::vector<lldb::ExpressionVariableSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ collection m_variables;
+
+public:
+ typedef AdaptedIterable<collection, lldb::ExpressionVariableSP,
+ vector_adapter>
+ ExpressionVariableListCollectionIterable;
+ ExpressionVariableListCollectionIterable Variables() {
+ return ExpressionVariableListCollectionIterable(m_variables);
+ }
};
class PersistentExpressionState : public ExpressionVariableList {
Index: lldb/include/lldb/Expression/Expression.h
===================================================================
--- lldb/include/lldb/Expression/Expression.h
+++ lldb/include/lldb/Expression/Expression.h
@@ -13,7 +13,7 @@
#include <string>
#include <vector>
-
+#include "lldb/Core/AddressRange.h"
#include "lldb/Expression/ExpressionTypeSystemHelper.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"
@@ -92,7 +92,11 @@
/// LLVM-style RTTI support.
ExpressionKind getKind() const { return m_kind; }
-
+
+ const AddressRange GetJITAddressRange(void) const {
+ return AddressRange(m_jit_start_addr, m_jit_end_addr - m_jit_start_addr);
+ }
+
private:
/// LLVM-style RTTI support.
const ExpressionKind m_kind;
Index: lldb/include/lldb/Core/Opcode.h
===================================================================
--- lldb/include/lldb/Core/Opcode.h
+++ lldb/include/lldb/Core/Opcode.h
@@ -221,9 +221,6 @@
// Get the opcode exactly as it would be laid out in memory.
uint32_t GetData(DataExtractor &data) const;
-protected:
- friend class lldb::SBInstruction;
-
const void *GetOpcodeDataBytes() const {
switch (m_type) {
case Opcode::eTypeInvalid:
@@ -243,6 +240,9 @@
return nullptr;
}
+protected:
+ friend class lldb::SBInstruction;
+
lldb::ByteOrder GetDataByteOrder() const;
bool GetEndianSwap() const {
Index: lldb/include/lldb/Core/Disassembler.h
===================================================================
--- lldb/include/lldb/Core/Disassembler.h
+++ lldb/include/lldb/Core/Disassembler.h
@@ -20,6 +20,7 @@
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Iterable.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private-enumerations.h"
@@ -311,10 +312,16 @@
private:
typedef std::vector<lldb::InstructionSP> collection;
+ collection m_instructions;
+
+public:
typedef collection::iterator iterator;
typedef collection::const_iterator const_iterator;
-
- collection m_instructions;
+ typedef AdaptedIterable<collection, lldb::InstructionSP, vector_adapter>
+ InstructionListCollectionIterable;
+ InstructionListCollectionIterable Instructions() {
+ return InstructionListCollectionIterable(m_instructions);
+ }
};
class PseudoInstruction : public Instruction {
Index: lldb/include/lldb/Breakpoint/BreakpointSite.h
===================================================================
--- lldb/include/lldb/Breakpoint/BreakpointSite.h
+++ lldb/include/lldb/Breakpoint/BreakpointSite.h
@@ -45,6 +45,16 @@
// display any breakpoint opcodes.
};
+ /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
+ enum BreakpointSiteKind {
+ eKindBreakpointSite,
+ eKindBreakpointInjectedSite,
+ };
+
+ static bool classof(const BreakpointSite *bp_site) {
+ return bp_site->getKind() == eKindBreakpointSite;
+ }
+
~BreakpointSite() override;
// This section manages the breakpoint traps
@@ -147,7 +157,7 @@
/// \param[in] thread
/// The thread against which to test.
///
- /// return
+ /// \return
/// \b true if the collection contains at least one location that
/// would be valid for this thread, false otherwise.
bool ValidForThisThread(Thread *thread);
@@ -187,6 +197,9 @@
BreakpointSite::Type GetType() const { return m_type; }
+ /// LLVM-style RTTI support.
+ BreakpointSiteKind getKind() const { return m_kind; }
+
void SetType(BreakpointSite::Type type) { m_type = type; }
private:
@@ -196,6 +209,7 @@
// a site, so let it be the one to manage setting the location hit count once
// and only once.
friend class StopInfoBreakpoint;
+ friend class BreakpointInjectedSite;
void BumpHitCounts();
@@ -221,13 +235,16 @@
std::recursive_mutex
m_owners_mutex; ///< This mutex protects the owners collection.
+ const BreakpointSiteKind m_kind;
+
static lldb::break_id_t GetNextID();
// Only the Process can create breakpoint sites in
// Process::CreateBreakpointSite (lldb::BreakpointLocationSP &, bool).
BreakpointSite(BreakpointSiteList *list,
const lldb::BreakpointLocationSP &owner, lldb::addr_t m_addr,
- bool use_hardware);
+ bool use_hardware,
+ BreakpointSiteKind kind = eKindBreakpointSite);
DISALLOW_COPY_AND_ASSIGN(BreakpointSite);
};
Index: lldb/include/lldb/Breakpoint/BreakpointOptions.h
===================================================================
--- lldb/include/lldb/Breakpoint/BreakpointOptions.h
+++ lldb/include/lldb/Breakpoint/BreakpointOptions.h
@@ -32,15 +32,16 @@
public:
enum OptionKind {
- eCallback = 1 << 0,
- eEnabled = 1 << 1,
- eOneShot = 1 << 2,
- eIgnoreCount = 1 << 3,
- eThreadSpec = 1 << 4,
- eCondition = 1 << 5,
- eAutoContinue = 1 << 6,
- eAllOptions = (eCallback | eEnabled | eOneShot | eIgnoreCount | eThreadSpec
- | eCondition | eAutoContinue)
+ eCallback = 1 << 0,
+ eEnabled = 1 << 1,
+ eOneShot = 1 << 2,
+ eIgnoreCount = 1 << 3,
+ eThreadSpec = 1 << 4,
+ eCondition = 1 << 5,
+ eAutoContinue = 1 << 6,
+ eInjectCondition = 1 << 7,
+ eAllOptions = (eCallback | eEnabled | eOneShot | eIgnoreCount |
+ eThreadSpec | eCondition | eAutoContinue | eInjectCondition)
};
struct CommandData {
CommandData()
@@ -113,9 +114,12 @@
/// \param[in] auto_continue
/// Should this breakpoint auto-continue after running its commands.
///
+ /// \param[in] inject_condition
+ /// Should the condition be injected and checked in-process.
+ ///
BreakpointOptions(const char *condition, bool enabled = true,
int32_t ignore = 0, bool one_shot = false,
- bool auto_continue = false);
+ bool auto_continue = false, bool inject_condition = false);
/// Breakpoints make options with all flags set. Locations and Names make
/// options with no flags set.
@@ -347,7 +351,20 @@
bool AnySet() const {
return m_set_flags.AnySet(eAllOptions);
}
-
+
+ /// Check if the breakpoint condition should be injected
+ ///
+ /// \return
+ /// If condition is injected \b true, \b false otherwise.
+ bool GetInjectCondition() const;
+
+ /// If \a inject_condition is \b true, inject the breakpoint condition in the
+ /// process.
+ void SetInjectCondition(bool inject_condition) {
+ m_inject_condition = inject_condition;
+ m_set_flags.Set(eInjectCondition);
+ }
+
protected:
// Classes that inherit from BreakpointOptions can see and modify these
bool IsOptionSet(OptionKind kind)
@@ -361,6 +378,7 @@
EnabledState,
OneShotState,
AutoContinue,
+ InjectCondition,
LastOptionName
};
static const char *g_option_names[(size_t)OptionNames::LastOptionName];
Index: lldb/include/lldb/Breakpoint/BreakpointLocation.h
===================================================================
--- lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -131,6 +131,16 @@
// condition has been set.
const char *GetConditionText(size_t *hash = nullptr) const;
+ /// Check if the breakpoint condition should be injected
+ ///
+ /// \return
+ /// If condition is injected \b true, \b false otherwise.
+ bool GetInjectCondition() const;
+
+ /// If \a inject_condition is \b true, inject the breakpoint condition in the
+ /// process.
+ void SetInjectCondition(bool inject_condition);
+
bool ConditionSaysStop(ExecutionContext &exe_ctx, Status &error);
/// Set the valid thread to be checked when the breakpoint is hit.
@@ -273,6 +283,7 @@
protected:
friend class BreakpointSite;
+ friend class BreakpointInjectedSite;
friend class BreakpointLocationList;
friend class Process;
friend class StopInfoBreakpoint;
Index: lldb/include/lldb/Breakpoint/BreakpointInjectedSite.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Breakpoint/BreakpointInjectedSite.h
@@ -0,0 +1,198 @@
+//===-- BreakpointInjectedSite.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_BreakpointInjectedSite_h_
+#define liblldb_BreakpointInjectedSite_h_
+
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+
+namespace lldb_private {
+
+/// \class BreakpointInjectedSite BreakpointInjectedSite.h
+/// Class that setup fast conditional breakpoints.
+///
+/// Fast conditional breakpoints have a different way of evaluating the
+/// condition expression by doing the check in-process, which saves the cost
+/// of doing several context switches between the inferior and LLDB.
+///
+///
+class BreakpointInjectedSite : public BreakpointSite {
+public:
+ /// LLVM-style RTTI support.
+ static bool classof(const BreakpointSite *bp_site) {
+ return bp_site->getKind() == eKindBreakpointInjectedSite;
+ }
+
+ // Destructor
+ ~BreakpointInjectedSite() override;
+
+ /// Fetch each breakpoint location's condition and build the JIT-ed condition
+ /// checker with the injected trap.
+ ///
+ /// \return
+ /// \b true if building the condition checker succeeded,
+ /// \b false otherwise.
+ bool BuildConditionExpression();
+
+ lldb_private::ExecutionContext GetOwnerExecutionContext() {
+ return m_owner_exe_ctx;
+ }
+
+ lldb::addr_t GetConditionExpressionAddress() {
+ return m_condition_expression_sp->StartAddress();
+ }
+
+ lldb::addr_t GetUtilityFunctionAddress() {
+ return m_create_args_struct_function_sp->StartAddress();
+ }
+
+ lldb::addr_t GetTrapAddress() {
+ return m_trap_addr.GetLoadAddress(m_target_sp.get());
+ }
+
+ /// \struct ArgumentMetadata BreakpointInjectedSite.h
+ /// "lldb/Breakpoint/BreakpointInjectedSite.h" Struct that contains debugging
+ /// information for the variable used in the condition expression.
+ struct VariableMetadata {
+
+ // Constructor
+
+ /// This constructor stores the variable name and type size and create a
+ /// DWARF Expression from the DataExtractor containing the DWARF Operation
+ /// and its operands.
+ ///
+ /// \param[in] name
+ /// The name of the variable.
+ ///
+ /// \param[in] size
+ /// The type size of the variable.
+ ///
+ /// \param[in] data
+ /// The buffer containing the variable DWARF Expression data.
+ ///
+ /// \param[in] version
+ /// The version of DWARF that is used for the DWARF Expression.
+ ///
+ /// \param[in] address_size
+ /// The size in bytes for the address of the current architecture.
+ ///
+ VariableMetadata(std::string name, size_t size, llvm::DataExtractor data,
+ uint16_t version, uint8_t address_size)
+ : name(std::move(name)), size(size),
+ dwarf(data, version, address_size) {}
+
+ /// The variable name.
+ std::string name;
+ /// The variable size.
+ size_t size;
+ /// The variable DWARF Expression.
+ llvm::DWARFExpression dwarf;
+ };
+
+private:
+ friend class Process;
+
+ // Constructor
+
+ /// This constructor stores the variable name and type size and create a
+ /// DWARF Expression from the DataExtractor containing the DWARF Operation
+ /// and its operands.
+ ///
+ /// \param[in] list
+ /// The list of the breakpoint sites already set.
+ ///
+ /// \param[in] owner
+ /// The breakpoint location holding this breakpoint site.
+ ///
+ /// \param[in] addr
+ /// The breakpoint site load address.
+ ///
+ BreakpointInjectedSite(BreakpointSiteList *list,
+ const lldb::BreakpointLocationSP &owner,
+ lldb::addr_t addr);
+
+ /// Scan the JIT-ed condition expression instructions and look for the
+ /// injected trap instruction.
+ ///
+ /// \param[in] jit
+ /// The buffer containing the JIT-ed condition expression.
+ ///
+ /// \param[in] size
+ /// The size of the JIT-ed condition expression in memory.
+ ///
+ /// \return
+ /// \b true if the injected trap instruction is found, \b false otherwise.
+ bool ResolveTrapAddress(void *jit, size_t size);
+
+ /// Iterate over the JIT-ed condition expression variable and build a metadata
+ /// vector used to resolve variables when checking the condition.
+ ///
+ /// \return
+ /// \b true if the metadata gathering succeeded, \b false otherwise.
+ bool GatherArgumentsMetadata();
+
+ /// Build the argument structure used by the JIT-ed condition expression.
+ /// Allocate dynamically the structure and using the variable metadata vector,
+ /// write the variable address in the argument structure.
+ ///
+ /// \return
+ /// \b true if building the argument structure succeeded,
+ /// \b false otherwise.
+ bool CreateArgumentsStructure();
+
+ /// Parse the variable's DWARF Expression and return the proper source code,
+ /// according to the DWARF Operation.
+ ///
+ /// \param[in] index
+ /// The index of the variable in the metadata vector.
+ ///
+ /// \param[in] error
+ /// The thread against which to test.
+ ///
+ /// \return
+ /// The source code needed to copy the variable in the argument structure.
+ std::string ParseDWARFExpression(size_t index, Status &error);
+
+private:
+ /// The target that hold the breakpoint.
+ lldb::TargetSP m_target_sp;
+ /// The breakpoint location load address.
+ Address m_real_addr;
+ /// The injected trap instruction address.
+ Address m_trap_addr;
+ /// The breakpoint location execution context.
+ lldb_private::ExecutionContext m_owner_exe_ctx;
+ /// The disassembler used to resolve the injected trap address.
+ lldb::DisassemblerSP m_disassembler_sp;
+ /// The JIT-ed condition checker.
+ lldb::UserExpressionSP m_condition_expression_sp;
+ /// The JIT-ed argument structure builder.
+ lldb::UtilityFunctionSP m_create_args_struct_function_sp;
+ /// The variable metadata vector.
+ std::vector<VariableMetadata> m_metadatas;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_BreakpointInjectedSite_h_
Index: lldb/include/lldb/Breakpoint/Breakpoint.h
===================================================================
--- lldb/include/lldb/Breakpoint/Breakpoint.h
+++ lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -63,6 +63,7 @@
/// \b Ignore Count
/// \b Callback
/// \b Condition
+/// \b Inject Condition
/// Note, these options can be set on the breakpoint, and they can also be set
/// on the individual locations. The options set on the breakpoint take
/// precedence over the options set on the individual location. So for
@@ -399,6 +400,16 @@
// condition has been set.
const char *GetConditionText() const;
+ /// If \a inject_condition is \b true, inject the breakpoint condition in the
+ /// process.
+ void SetInjectCondition(bool inject_condition);
+
+ /// Check if the breakpoint condition should be injected
+ ///
+ /// \return
+ /// If condition is injected \b true, \b false otherwise.
+ bool GetInjectCondition() const;
+
// The next section are various utility functions.
/// Return the number of breakpoint locations that have resolved to actual
Index: lldb/include/lldb/API/SBBreakpointLocation.h
===================================================================
--- lldb/include/lldb/API/SBBreakpointLocation.h
+++ lldb/include/lldb/API/SBBreakpointLocation.h
@@ -53,6 +53,10 @@
bool GetAutoContinue();
+ void SetInjectCondition(bool inject_condition);
+
+ bool GetInjectCondition();
+
void SetScriptCallbackFunction(const char *callback_function_name);
SBError SetScriptCallbackBody(const char *script_body_text);
Index: lldb/include/lldb/API/SBBreakpoint.h
===================================================================
--- lldb/include/lldb/API/SBBreakpoint.h
+++ lldb/include/lldb/API/SBBreakpoint.h
@@ -74,6 +74,10 @@
bool GetAutoContinue();
+ void SetInjectCondition(bool inject_condition);
+
+ bool GetInjectCondition();
+
void SetThreadID(lldb::tid_t sb_thread_id);
lldb::tid_t GetThreadID();
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits