diff options
| author | Jim Ingham <jingham@apple.com> | 2017-09-14 20:22:49 +0000 |
|---|---|---|
| committer | Jim Ingham <jingham@apple.com> | 2017-09-14 20:22:49 +0000 |
| commit | b842f2ecf005db63713e5d939b9a2a10d912cb2b (patch) | |
| tree | 2a35a44b6a54b12f4d373bff208778004eae72fe /lldb/source | |
| parent | b2388c52e8efdaa34818ae9798d434c0d5e88814 (diff) | |
| download | bcm5719-llvm-b842f2ecf005db63713e5d939b9a2a10d912cb2b.tar.gz bcm5719-llvm-b842f2ecf005db63713e5d939b9a2a10d912cb2b.zip | |
Make breakpoint names real entities.
When introduced, breakpoint names were just tags that you could
apply to breakpoints that would allow you to refer to a breakpoint
when you couldn't capture the ID, or to refer to a collection of
breakpoints.
This change makes the names independent holders of breakpoint options
that you can then apply to breakpoints when you add the name to the
breakpoint. It adds the "breakpoint name configure" command to set
up or reconfigure breakpoint names. There is also full support for
then in the SB API, including a new SBBreakpointName class.
The connection between the name and the breakpoints
sharing the name remains live, so if you reconfigure the name, all the
breakpoint options all change as well. This allows a quick way
to share complex breakpoint behavior among a bunch of breakpoints, and
a convenient way to iterate on the set.
You can also create a name from a breakpoint, allowing a quick way
to copy options from one breakpoint to another.
I also added the ability to make hidden and delete/disable protected
names. When applied to a breakpoint, you will only be able to list,
delete or disable that breakpoint if you refer to it explicitly by ID.
This feature will allow GUI's that need to use breakpoints for their
own purposes to keep their breakpoints from getting accidentally
disabled or deleted.
<rdar://problem/22094452>
llvm-svn: 313292
Diffstat (limited to 'lldb/source')
18 files changed, 1827 insertions, 558 deletions
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index 9abdce0c928..6a0ff9536c2 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -37,6 +37,8 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" +#include "SBBreakpointOptionCommon.h" + #include "lldb/lldb-enumerations.h" #include "llvm/ADT/STLExtras.h" @@ -44,21 +46,6 @@ using namespace lldb; using namespace lldb_private; -struct CallbackData { - SBBreakpoint::BreakpointHitCallback callback; - void *callback_baton; -}; - -class SBBreakpointCallbackBaton : public TypedBaton<CallbackData> { -public: - SBBreakpointCallbackBaton(SBBreakpoint::BreakpointHitCallback callback, - void *baton) - : TypedBaton(llvm::make_unique<CallbackData>()) { - getItem()->callback = callback; - getItem()->callback_baton = baton; - } -}; - SBBreakpoint::SBBreakpoint() {} SBBreakpoint::SBBreakpoint(const SBBreakpoint &rhs) @@ -500,37 +487,9 @@ bool SBBreakpoint::GetDescription(SBStream &s, bool include_locations) { return false; } -bool SBBreakpoint::PrivateBreakpointHitCallback(void *baton, - StoppointCallbackContext *ctx, - lldb::user_id_t break_id, - lldb::user_id_t break_loc_id) { - ExecutionContext exe_ctx(ctx->exe_ctx_ref); - BreakpointSP bp_sp( - exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id)); - if (baton && bp_sp) { - CallbackData *data = (CallbackData *)baton; - lldb_private::Breakpoint *bp = bp_sp.get(); - if (bp && data->callback) { - Process *process = exe_ctx.GetProcessPtr(); - if (process) { - SBProcess sb_process(process->shared_from_this()); - SBThread sb_thread; - SBBreakpointLocation sb_location; - assert(bp_sp); - sb_location.SetLocation(bp_sp->FindLocationByID(break_loc_id)); - Thread *thread = exe_ctx.GetThreadPtr(); - if (thread) - sb_thread.SetThread(thread->shared_from_this()); - - return data->callback(data->callback_baton, sb_process, sb_thread, - sb_location); - } - } - } - return true; // Return true if we should stop at this breakpoint -} - -void SBBreakpoint::SetCallback(BreakpointHitCallback callback, void *baton) { +void SBBreakpoint + ::SetCallback(SBBreakpointHitCallback callback, + void *baton) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); BreakpointSP bkpt_sp = GetSP(); LLDB_LOG(log, "breakpoint = {0}, callback = {1}, baton = {2}", bkpt_sp.get(), @@ -540,7 +499,8 @@ void SBBreakpoint::SetCallback(BreakpointHitCallback callback, void *baton) { std::lock_guard<std::recursive_mutex> guard( bkpt_sp->GetTarget().GetAPIMutex()); BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton)); - bkpt_sp->SetCallback(SBBreakpoint::PrivateBreakpointHitCallback, baton_sp, + bkpt_sp->SetCallback(SBBreakpointCallbackBaton + ::PrivateBreakpointHitCallback, baton_sp, false); } } @@ -599,10 +559,17 @@ bool SBBreakpoint::AddName(const char *new_name) { bkpt_sp->GetTarget().GetAPIMutex()); Status error; // Think I'm just going to swallow the error here, it's // probably more annoying to have to provide it. - return bkpt_sp->AddName(new_name, error); + bkpt_sp->GetTarget().AddNameToBreakpoint(bkpt_sp, new_name, error); + if (error.Fail()) + { + if (log) + log->Printf("Failed to add name: '%s' to breakpoint: %s", + new_name, error.AsCString()); + return false; + } } - return false; + return true; } void SBBreakpoint::RemoveName(const char *name_to_remove) { @@ -613,7 +580,8 @@ void SBBreakpoint::RemoveName(const char *name_to_remove) { if (bkpt_sp) { std::lock_guard<std::recursive_mutex> guard( bkpt_sp->GetTarget().GetAPIMutex()); - bkpt_sp->RemoveName(name_to_remove); + bkpt_sp->GetTarget().RemoveNameFromBreakpoint(bkpt_sp, + ConstString(name_to_remove)); } } diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp new file mode 100644 index 00000000000..b2ed5e71699 --- /dev/null +++ b/lldb/source/API/SBBreakpointName.cpp @@ -0,0 +1,662 @@ +//===-- SBBreakpointName.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/API/SBBreakpointName.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBTarget.h" + +#include "lldb/Breakpoint/BreakpointName.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +#include "SBBreakpointOptionCommon.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb +{ +class SBBreakpointNameImpl { +public: + SBBreakpointNameImpl(SBTarget &sb_target, const char *name) + { + if (!name || name[0] == '\0') + return; + m_name.assign(name); + + if (!sb_target.IsValid()) + return; + + TargetSP target_sp = sb_target.GetSP(); + if (!target_sp) + return; + + m_target_wp = target_sp; + } + + SBBreakpointNameImpl(TargetSP target_sp, const char *name) + { + if (!name || name[0] == '\0') + return; + m_name.assign(name); + + if (!target_sp) + return; + + m_target_wp = target_sp; + } + + bool operator==(const SBBreakpointNameImpl &rhs) { + return m_name == rhs.m_name + && m_target_wp.lock() == rhs.m_target_wp.lock(); + } + + bool operator!=(const SBBreakpointNameImpl &rhs) { + return m_name != rhs.m_name + || m_target_wp.lock() != rhs.m_target_wp.lock(); + } + // For now we take a simple approach and only keep the name, and relook + // up the location when we need it. + + TargetSP GetTarget() { + return m_target_wp.lock(); + } + + const char *GetName() { + return m_name.c_str(); + } + + bool IsValid() { + return !m_name.empty() && m_target_wp.lock(); + } + + lldb_private::BreakpointName *GetBreakpointName() + { + if (!IsValid()) + return nullptr; + TargetSP target_sp = GetTarget(); + if (!target_sp) + return nullptr; + Status error; + return target_sp->FindBreakpointName(ConstString(m_name), true, error); + } + + const lldb_private::BreakpointName *GetBreakpointName() const + { + return GetBreakpointName(); + } + +private: + TargetWP m_target_wp; + std::string m_name; +}; +} // namespace lldb + +SBBreakpointName::SBBreakpointName() {} + +SBBreakpointName::SBBreakpointName(SBTarget &sb_target, const char *name) +{ + m_impl_up.reset(new SBBreakpointNameImpl(sb_target, name)); + // Call FindBreakpointName here to make sure the name is valid, reset if + // not: + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + m_impl_up.reset(); +} + +SBBreakpointName::SBBreakpointName(SBBreakpoint &sb_bkpt, const char *name) +{ + if (!sb_bkpt.IsValid()) { + m_impl_up.reset(); + return; + } + BreakpointSP bkpt_sp = sb_bkpt.GetSP(); + Target &target = bkpt_sp->GetTarget(); + + m_impl_up.reset(new SBBreakpointNameImpl(target.shared_from_this(), name)); + + // Call FindBreakpointName here to make sure the name is valid, reset if + // not: + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) { + m_impl_up.reset(); + return; + } + + // Now copy over the breakpoint's options: + target.ConfigureBreakpointName(*bp_name, *bkpt_sp->GetOptions(), + BreakpointName::Permissions()); +} + +SBBreakpointName::SBBreakpointName(const SBBreakpointName &rhs) +{ + if (!rhs.m_impl_up) + return; + else + m_impl_up.reset(new SBBreakpointNameImpl(rhs.m_impl_up->GetTarget(), + rhs.m_impl_up->GetName())); +} + +SBBreakpointName::~SBBreakpointName() = default; + +const SBBreakpointName &SBBreakpointName::operator=(const SBBreakpointName &rhs) +{ + if (!rhs.m_impl_up) { + m_impl_up.reset(); + return *this; + } + + m_impl_up.reset(new SBBreakpointNameImpl(rhs.m_impl_up->GetTarget(), + rhs.m_impl_up->GetName())); + return *this; +} + +bool SBBreakpointName::operator==(const lldb::SBBreakpointName &rhs) { + return *m_impl_up.get() == *rhs.m_impl_up.get(); +} + +bool SBBreakpointName::operator!=(const lldb::SBBreakpointName &rhs) { + return *m_impl_up.get() != *rhs.m_impl_up.get(); +} + +bool SBBreakpointName::IsValid() const { + if (!m_impl_up) + return false; + return m_impl_up->IsValid(); +} + +const char *SBBreakpointName::GetName() const { + if (!m_impl_up) + return "<Invalid Breakpoint Name Object>"; + return m_impl_up->GetName(); +} + +void SBBreakpointName::SetEnabled(bool enable) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} enabled: {1}\n", bp_name->GetName(), enable); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetEnabled(enable); +} + +void SBBreakpointName::UpdateName(BreakpointName &bp_name) { + if (!IsValid()) + return; + + TargetSP target_sp = m_impl_up->GetTarget(); + if (!target_sp) + return; + target_sp->ApplyNameToBreakpoints(bp_name); + +} + +bool SBBreakpointName::IsEnabled() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().IsEnabled(); +} + +void SBBreakpointName::SetOneShot(bool one_shot) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} one_shot: {1}\n", bp_name->GetName(), one_shot); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetOneShot(one_shot); + UpdateName(*bp_name); +} + +bool SBBreakpointName::IsOneShot() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + const BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().IsOneShot(); +} + +void SBBreakpointName::SetIgnoreCount(uint32_t count) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} one_shot: {1}\n", bp_name->GetName(), count); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetIgnoreCount(count); + UpdateName(*bp_name); +} + +uint32_t SBBreakpointName::GetIgnoreCount() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetIgnoreCount(); +} + +void SBBreakpointName::SetCondition(const char *condition) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} one_shot: {1}\n", bp_name->GetName(), + condition ? condition : "<NULL>"); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetCondition(condition); + UpdateName(*bp_name); +} + +const char *SBBreakpointName::GetCondition() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return nullptr; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetConditionText(); +} + +void SBBreakpointName::SetAutoContinue(bool auto_continue) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} auto-continue: {1}\n", bp_name->GetName(), auto_continue); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetAutoContinue(auto_continue); + UpdateName(*bp_name); +} + +bool SBBreakpointName::GetAutoContinue() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return nullptr; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().IsAutoContinue(); +} + +void SBBreakpointName::SetThreadID(tid_t tid) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} tid: {1:x}\n", bp_name->GetName(), tid); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().SetThreadID(tid); + UpdateName(*bp_name); +} + +tid_t SBBreakpointName::GetThreadID() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return LLDB_INVALID_THREAD_ID; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetThreadSpec()->GetTID(); +} + +void SBBreakpointName::SetThreadIndex(uint32_t index) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} thread index: {1}\n", bp_name->GetName(), index); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().GetThreadSpec()->SetIndex(index); + UpdateName(*bp_name); +} + +uint32_t SBBreakpointName::GetThreadIndex() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return LLDB_INVALID_THREAD_ID; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetThreadSpec()->GetIndex(); +} + +void SBBreakpointName::SetThreadName(const char *thread_name) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} thread name: {1}\n", bp_name->GetName(), thread_name); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().GetThreadSpec()->SetName(thread_name); + UpdateName(*bp_name); +} + +const char *SBBreakpointName::GetThreadName() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return nullptr; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetThreadSpec()->GetName(); +} + +void SBBreakpointName::SetQueueName(const char *queue_name) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} queue name: {1}\n", bp_name->GetName(), queue_name); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + bp_name->GetOptions().GetThreadSpec()->SetQueueName(queue_name); + UpdateName(*bp_name); +} + +const char *SBBreakpointName::GetQueueName() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return nullptr; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + return bp_name->GetOptions().GetThreadSpec()->GetQueueName(); +} + +void SBBreakpointName::SetCommandLineCommands(SBStringList &commands) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + if (commands.GetSize() == 0) + return; + + LLDB_LOG(log, "Name: {0} commands\n", bp_name->GetName()); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up( + new BreakpointOptions::CommandData(*commands, eScriptLanguageNone)); + + bp_name->GetOptions().SetCommandDataCallback(cmd_data_up); + UpdateName(*bp_name); +} + +bool SBBreakpointName::GetCommandLineCommands(SBStringList &commands) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + StringList command_list; + bool has_commands = + bp_name->GetOptions().GetCommandLineCallbacks(command_list); + if (has_commands) + commands.AppendList(command_list); + return has_commands; +} + +bool SBBreakpointName::GetDescription(SBStream &s) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + { + s.Printf("No value"); + return false; + } + + LLDB_LOG(log, "Name: {0}\n", bp_name->GetName()); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + bp_name->GetDescription(s.get(), eDescriptionLevelFull); + return true; +} + +void SBBreakpointName::SetCallback(SBBreakpointHitCallback callback, + void *baton) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + LLDB_LOG(log, "callback = {1}, baton = {2}", callback, baton); + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton)); + bp_name->GetOptions().SetCallback(SBBreakpointCallbackBaton + ::PrivateBreakpointHitCallback, + baton_sp, + false); + UpdateName(*bp_name); +} + +void SBBreakpointName::SetScriptCallbackFunction( + const char *callback_function_name) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + + LLDB_LOG(log, "Name: {0} callback: {1}\n", bp_name->GetName(), + callback_function_name); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + BreakpointOptions &bp_options = bp_name->GetOptions(); + m_impl_up->GetTarget() + ->GetDebugger() + .GetCommandInterpreter() + .GetScriptInterpreter() + ->SetBreakpointCommandCallbackFunction(&bp_options, + callback_function_name); + UpdateName(*bp_name); +} + +SBError SBBreakpointName::SetScriptCallbackBody(const char *callback_body_text) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + SBError sb_error; + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return sb_error; + + LLDB_LOG(log, "Name: {0} callback: {1}\n", bp_name->GetName(), + callback_body_text); + + std::lock_guard<std::recursive_mutex> guard( + m_impl_up->GetTarget()->GetAPIMutex()); + + BreakpointOptions &bp_options = bp_name->GetOptions(); + Status error = + m_impl_up->GetTarget() + ->GetDebugger() + .GetCommandInterpreter() + .GetScriptInterpreter() + ->SetBreakpointCommandCallback(&bp_options, callback_body_text); + sb_error.SetError(error); + if (!sb_error.Fail()) + UpdateName(*bp_name); + + return sb_error; +} + +bool SBBreakpointName::GetAllowList() const +{ + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + return bp_name->GetPermissions().GetAllowList(); +} + +void SBBreakpointName::SetAllowList(bool value) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + if (log) + log->Printf("Setting allow list to %u for %s.", value, + bp_name->GetName().AsCString()); + bp_name->GetPermissions().SetAllowList(value); +} + +bool SBBreakpointName::GetAllowDelete() +{ + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + return bp_name->GetPermissions().GetAllowDelete(); +} + +void SBBreakpointName::SetAllowDelete(bool value) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + if (log) + log->Printf("Setting allow delete to %u for %s.", value, + bp_name->GetName().AsCString()); + bp_name->GetPermissions().SetAllowDelete(value); +} + +bool SBBreakpointName::GetAllowDisable() +{ + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return false; + return bp_name->GetPermissions().GetAllowDisable(); +} + +void SBBreakpointName::SetAllowDisable(bool value) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + BreakpointName *bp_name = GetBreakpointName(); + if (!bp_name) + return; + if (log) + log->Printf("Setting allow disable to %u for %s.", value, + bp_name->GetName().AsCString()); + bp_name->GetPermissions().SetAllowDisable(value); +} + +lldb_private::BreakpointName *SBBreakpointName::GetBreakpointName() const +{ + if (!IsValid()) + return nullptr; + return m_impl_up->GetBreakpointName(); +} + diff --git a/lldb/source/API/SBBreakpointOptionCommon.cpp b/lldb/source/API/SBBreakpointOptionCommon.cpp new file mode 100644 index 00000000000..2a12491d6ec --- /dev/null +++ b/lldb/source/API/SBBreakpointOptionCommon.cpp @@ -0,0 +1,84 @@ +//===-- SBBreakpointName.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/API/SBBreakpointName.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBThread.h" + +#include "lldb/Breakpoint/BreakpointName.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +#include "lldb/lldb-enumerations.h" + +#include "SBBreakpointOptionCommon.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +SBBreakpointCallbackBaton::SBBreakpointCallbackBaton(SBBreakpointHitCallback + callback, + void *baton) + : TypedBaton(llvm::make_unique<CallbackData>()) { + getItem()->callback = callback; + getItem()->callback_baton = baton; + } + + bool SBBreakpointCallbackBaton::PrivateBreakpointHitCallback(void *baton, + StoppointCallbackContext *ctx, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) +{ + ExecutionContext exe_ctx(ctx->exe_ctx_ref); + BreakpointSP bp_sp( + exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id)); + if (baton && bp_sp) { + CallbackData *data = (CallbackData *)baton; + lldb_private::Breakpoint *bp = bp_sp.get(); + if (bp && data->callback) { + Process *process = exe_ctx.GetProcessPtr(); + if (process) { + SBProcess sb_process(process->shared_from_this()); + SBThread sb_thread; + SBBreakpointLocation sb_location; + assert(bp_sp); + sb_location.SetLocation(bp_sp->FindLocationByID(break_loc_id)); + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread) + sb_thread.SetThread(thread->shared_from_this()); + + return data->callback(data->callback_baton, sb_process, sb_thread, + sb_location); + } + } + } + return true; // Return true if we should stop at this breakpoint +} + diff --git a/lldb/source/API/SBBreakpointOptionCommon.h b/lldb/source/API/SBBreakpointOptionCommon.h new file mode 100644 index 00000000000..212fe398dd7 --- /dev/null +++ b/lldb/source/API/SBBreakpointOptionCommon.h @@ -0,0 +1,35 @@ +//===-- SBBreakpointOptionCommon.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBreakpointOptionCommons_h_ +#define LLDB_SBBreakpointOptionCommons_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/Utility/Baton.h" + +namespace lldb +{ +struct CallbackData { + SBBreakpointHitCallback callback; + void *callback_baton; +}; + +class SBBreakpointCallbackBaton : public lldb_private::TypedBaton<CallbackData> { +public: + SBBreakpointCallbackBaton(SBBreakpointHitCallback callback, + void *baton); + + static bool PrivateBreakpointHitCallback(void *baton, + lldb_private::StoppointCallbackContext *ctx, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); +}; + +} // namespace lldb +#endif // LLDB_SBBreakpointOptionCommons_h_ diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index cf6a27a049b..0185f1e9ec4 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -622,6 +622,20 @@ SBTarget SBDebugger::CreateTarget(const char *filename) { return sb_target; } +SBTarget SBDebugger::GetDummyTarget() { + SBTarget sb_target; + if (m_opaque_sp) { + sb_target.SetSP(m_opaque_sp->GetDummyTarget()->shared_from_this()); + } + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + if (log) + log->Printf( + "SBDebugger(%p)::GetDummyTarget() => SBTarget(%p)", + static_cast<void *>(m_opaque_sp.get()), + static_cast<void *>(sb_target.GetSP().get())); + return sb_target; +} + bool SBDebugger::DeleteTarget(lldb::SBTarget &target) { bool result = false; if (m_opaque_sp) { diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 417a15074c7..93869d71c62 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1087,11 +1087,38 @@ bool SBTarget::FindBreakpointsByName(const char *name, return true; } +void SBTarget::GetBreakpointNames(SBStringList &names) +{ + names.Clear(); + + TargetSP target_sp(GetSP()); + if (target_sp) { + std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); + + std::vector<std::string> name_vec; + target_sp->GetBreakpointNames(name_vec); + for (auto name : name_vec) + names.AppendString(name.c_str()); + } +} + +void SBTarget::DeleteBreakpointName(const char *name) +{ + TargetSP target_sp(GetSP()); + if (target_sp) { + std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); + + std::vector<std::string> name_vec; + target_sp->DeleteBreakpointName(ConstString(name)); + + } +} + bool SBTarget::EnableAllBreakpoints() { TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); - target_sp->EnableAllBreakpoints(); + target_sp->EnableAllowedBreakpoints(); return true; } return false; @@ -1101,7 +1128,7 @@ bool SBTarget::DisableAllBreakpoints() { TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); - target_sp->DisableAllBreakpoints(); + target_sp->DisableAllowedBreakpoints(); return true; } return false; @@ -1111,7 +1138,7 @@ bool SBTarget::DeleteAllBreakpoints() { TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); - target_sp->RemoveAllBreakpoints(); + target_sp->RemoveAllowedBreakpoints(); return true; } return false; diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp index fd606aa83d6..3f6c63e1e5b 100644 --- a/lldb/source/Breakpoint/Breakpoint.cpp +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -210,7 +210,7 @@ lldb::BreakpointSP Breakpoint::CreateFromStructuredData( llvm::StringRef name; Status error; success = names_array->GetItemAtIndexAsString(i, name); - result_sp->AddName(name, error); + target.AddNameToBreakpoint(result_sp, name.str().c_str(), error); } } @@ -455,6 +455,10 @@ bool Breakpoint::InvokeCallback(StoppointCallbackContext *context, BreakpointOptions *Breakpoint::GetOptions() { return m_options_up.get(); } +const BreakpointOptions *Breakpoint::GetOptions() const { + return m_options_up.get(); +} + void Breakpoint::ResolveBreakpoint() { if (m_resolver_sp) m_resolver_sp->ResolveBreakpoint(*m_filter_sp); @@ -841,18 +845,8 @@ size_t Breakpoint::GetNumResolvedLocations() const { size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); } -bool Breakpoint::AddName(llvm::StringRef new_name, Status &error) { - if (new_name.empty()) - return false; - if (!BreakpointID::StringIsBreakpointName(new_name, error)) { - error.SetErrorStringWithFormatv("input name \"{0}\" not a breakpoint name.", - new_name); - return false; - } - if (!error.Success()) - return false; - - m_name_list.insert(new_name); +bool Breakpoint::AddName(llvm::StringRef new_name) { + m_name_list.insert(new_name.str().c_str()); return true; } diff --git a/lldb/source/Breakpoint/BreakpointID.cpp b/lldb/source/Breakpoint/BreakpointID.cpp index 112f7c0b519..b8010654682 100644 --- a/lldb/source/Breakpoint/BreakpointID.cpp +++ b/lldb/source/Breakpoint/BreakpointID.cpp @@ -101,15 +101,24 @@ BreakpointID::ParseCanonicalReference(llvm::StringRef input) { bool BreakpointID::StringIsBreakpointName(llvm::StringRef str, Status &error) { error.Clear(); if (str.empty()) + { + error.SetErrorStringWithFormat("Empty breakpoint names are not allowed"); return false; + } // First character must be a letter or _ if (!isalpha(str[0]) && str[0] != '_') + { + error.SetErrorStringWithFormat("Breakpoint names must start with a " + "character or underscore: %s", + str.str().c_str()); return false; + } // Cannot contain ., -, or space. if (str.find_first_of(".- ") != llvm::StringRef::npos) { - error.SetErrorStringWithFormat("invalid breakpoint name: \"%s\"", + error.SetErrorStringWithFormat("Breakpoint names cannot contain " + "'.' or '-': \"%s\"", str.str().c_str()); return false; } diff --git a/lldb/source/Breakpoint/BreakpointIDList.cpp b/lldb/source/Breakpoint/BreakpointIDList.cpp index 7b461147a4e..0a704fdc918 100644 --- a/lldb/source/Breakpoint/BreakpointIDList.cpp +++ b/lldb/source/Breakpoint/BreakpointIDList.cpp @@ -11,6 +11,7 @@ // C++ Includes // Other libraries and framework includes // Project includes +#include "lldb/lldb-enumerations.h" #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/Breakpoint.h" @@ -117,6 +118,8 @@ void BreakpointIDList::InsertStringArray(const char **string_array, void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target, bool allow_locations, + BreakpointName::Permissions + ::PermissionKinds purpose, CommandReturnObject &result, Args &new_args) { llvm::StringRef range_from; @@ -302,14 +305,29 @@ void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target, } // Okay, now see if we found any names, and if we did, add them: - if (target && names_found.size()) { - for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) { - for (std::string name : names_found) { - if (bkpt_sp->MatchesName(name.c_str())) { - StreamString canonical_id_str; - BreakpointID::GetCanonicalReference( - &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID); - new_args.AppendArgument(canonical_id_str.GetString()); + if (target && !names_found.empty()) { + Status error; + // Remove any names that aren't visible for this purpose: + auto iter = names_found.begin(); + while (iter != names_found.end()) { + BreakpointName *bp_name = target->FindBreakpointName(ConstString(*iter), + true, + error); + if (bp_name && !bp_name->GetPermission(purpose)) + iter = names_found.erase(iter); + else + iter++; + } + + if (!names_found.empty()) { + for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) { + for (std::string name : names_found) { + if (bkpt_sp->MatchesName(name.c_str())) { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference( + &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID); + new_args.AppendArgument(canonical_id_str.GetString()); + } } } } diff --git a/lldb/source/Breakpoint/BreakpointList.cpp b/lldb/source/Breakpoint/BreakpointList.cpp index 15bcb34a3d8..01ac59f0a90 100644 --- a/lldb/source/Breakpoint/BreakpointList.cpp +++ b/lldb/source/Breakpoint/BreakpointList.cpp @@ -71,6 +71,13 @@ void BreakpointList::SetEnabledAll(bool enabled) { bp_sp->SetEnabled(enabled); } +void BreakpointList::SetEnabledAllowed(bool enabled) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + for (const auto &bp_sp : m_breakpoints) + if (bp_sp->AllowDisable()) + bp_sp->SetEnabled(enabled); +} + void BreakpointList::RemoveAll(bool notify) { std::lock_guard<std::recursive_mutex> guard(m_mutex); ClearAllBreakpointSites(); @@ -90,6 +97,32 @@ void BreakpointList::RemoveAll(bool notify) { m_breakpoints.erase(m_breakpoints.begin(), m_breakpoints.end()); } +void BreakpointList::RemoveAllowed(bool notify) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + bp_collection::iterator pos, end = m_breakpoints.end(); + if (notify) { + for (pos = m_breakpoints.begin(); pos != end; ++pos) { + if(!(*pos)->AllowDelete()) + continue; + if ((*pos)->GetTarget().EventTypeHasListeners( + Target::eBroadcastBitBreakpointChanged)) { + (*pos)->GetTarget().BroadcastEvent( + Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData(eBreakpointEventTypeRemoved, + *pos)); + } + } + } + pos = m_breakpoints.begin(); + while ( pos != end) { + if((*pos)->AllowDelete()) + pos = m_breakpoints.erase(pos); + else + pos++; + } +} + class BreakpointIDMatches { public: BreakpointIDMatches(break_id_t break_id) : m_break_id(break_id) {} diff --git a/lldb/source/Breakpoint/BreakpointName.cpp b/lldb/source/Breakpoint/BreakpointName.cpp new file mode 100644 index 00000000000..7b9a2acfbc4 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointName.cpp @@ -0,0 +1,88 @@ +//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details-> +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/Casting.h" + +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointOptions.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +const Flags::ValueType BreakpointName::Permissions::permissions_mask + [BreakpointName::Permissions::PermissionKinds::allPerms + 1] = { + (1u << 0), + (1u << 1), + (1u << 2), + (0x5u) +}; + +BreakpointName::BreakpointName(const ConstString &name, const Breakpoint &bkpt, + const char *help) : + m_name(name), m_options(bkpt.GetOptions()) +{ + SetHelp(help); +} + +bool BreakpointName::Permissions::GetDescription(Stream *s, + lldb::DescriptionLevel level) { + if (!AnySet()) + return false; + s->IndentMore(); + s->Indent(); + if (IsSet(listPerm)) + s->Printf("list: %s", GetAllowList() ? "allowed" : "disallowed"); + + if (IsSet(disablePerm)) + s->Printf("disable: %s", GetAllowDisable() ? "allowed" : "disallowed"); + + if (IsSet(deletePerm)) + s->Printf("delete: %s", GetAllowDelete() ? "allowed" : "disallowed"); + s->IndentLess(); + return true; +} + +bool BreakpointName::GetDescription(Stream *s, lldb::DescriptionLevel level) { + bool printed_any = false; + if (GetOptions().AnySet()) + { + s->PutCString("Options: \n"); + s->IndentMore(); + s->Indent(); + GetOptions().GetDescription(s, level); + printed_any = true; + s->IndentLess(); + } + if (GetPermissions().AnySet()) + { + s->PutCString("Permissions: \n"); + s->IndentMore(); + s->Indent(); + GetPermissions().GetDescription(s, level); + printed_any = true; + s->IndentLess(); + } + return printed_any; +} + +void BreakpointName::ConfigureBreakpoint(lldb::BreakpointSP bp_sp) +{ + bp_sp->GetOptions()->CopyOverSetOptions(GetOptions()); + bp_sp->GetPermissions().MergeInto(GetPermissions()); +} diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp index 7159688102d..662b288794d 100644 --- a/lldb/source/Breakpoint/BreakpointOptions.cpp +++ b/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -132,7 +132,7 @@ BreakpointOptions::BreakpointOptions(bool all_flags_set) 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_ap(), m_condition_text(), m_condition_text_hash(0), m_auto_continue(false), - m_set_flags() { + m_set_flags(0) { if (all_flags_set) m_set_flags.Set(~((Flags::ValueType) 0)); } @@ -142,11 +142,14 @@ BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, bool auto_continue) : 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(condition), + 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 - | eCondition | eAutoContinue); + | eAutoContinue); + if (condition && *condition != '\0') { + SetCondition(condition); + } } //---------------------------------------------------------------------- @@ -187,6 +190,59 @@ operator=(const BreakpointOptions &rhs) { return *this; } +void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming) +{ + if (incoming.m_set_flags.Test(eEnabled)) + { + m_enabled = incoming.m_enabled; + m_set_flags.Set(eEnabled); + } + if (incoming.m_set_flags.Test(eOneShot)) + { + m_one_shot = incoming.m_one_shot; + m_set_flags.Set(eOneShot); + } + if (incoming.m_set_flags.Test(eCallback)) + { + m_callback = incoming.m_callback; + m_callback_baton_sp = incoming.m_callback_baton_sp; + m_callback_is_synchronous = incoming.m_callback_is_synchronous; + m_baton_is_command_baton = incoming.m_baton_is_command_baton; + m_set_flags.Set(eCallback); + } + if (incoming.m_set_flags.Test(eIgnoreCount)) + { + m_ignore_count = incoming.m_ignore_count; + m_set_flags.Set(eIgnoreCount); + } + if (incoming.m_set_flags.Test(eCondition)) + { + // If we're copying over an empty condition, mark it as unset. + if (incoming.m_condition_text.empty()) { + m_condition_text.clear(); + m_condition_text_hash = 0; + m_set_flags.Clear(eCondition); + } else { + m_condition_text = incoming.m_condition_text; + m_condition_text_hash = incoming.m_condition_text_hash; + m_set_flags.Set(eCondition); + } + } + if (incoming.m_set_flags.Test(eAutoContinue)) + { + m_auto_continue = incoming.m_auto_continue; + m_set_flags.Set(eAutoContinue); + } + if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_ap) + { + if (!m_thread_spec_ap) + m_thread_spec_ap.reset(new ThreadSpec(*incoming.m_thread_spec_ap.get())); + else + *m_thread_spec_ap.get() = *incoming.m_thread_spec_ap.get(); + m_set_flags.Set(eThreadSpec); + } +} + //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- @@ -327,23 +383,23 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { StructuredData::DictionarySP options_dict_sp( new StructuredData::Dictionary()); - if (m_set_flags.Set(eEnabled)) + if (m_set_flags.Test(eEnabled)) options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled); - if (m_set_flags.Set(eOneShot)) + if (m_set_flags.Test(eOneShot)) options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), m_one_shot); - if (m_set_flags.Set(eAutoContinue)) + if (m_set_flags.Test(eAutoContinue)) options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue), m_auto_continue); - if (m_set_flags.Set(eIgnoreCount)) + if (m_set_flags.Test(eIgnoreCount)) options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), m_ignore_count); - if (m_set_flags.Set(eCondition)) + if (m_set_flags.Test(eCondition)) options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), m_condition_text); - if (m_set_flags.Set(eCallback) && m_baton_is_command_baton) { + if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) { auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); StructuredData::ObjectSP commands_sp = @@ -353,7 +409,7 @@ StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); } } - if (m_set_flags.Set(eThreadSpec) && m_thread_spec_ap) { + if (m_set_flags.Test(eThreadSpec) && m_thread_spec_ap) { StructuredData::ObjectSP thread_spec_sp = m_thread_spec_ap->SerializeToStructuredData(); options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp); @@ -618,3 +674,18 @@ bool BreakpointOptions::BreakpointOptionsCallbackFunction( } return ret_value; } + +void BreakpointOptions::Clear() +{ + m_set_flags.Clear(); + m_thread_spec_ap.release(); + m_one_shot = false; + m_ignore_count = 0; + m_auto_continue = false; + m_callback = nullptr; + m_callback_baton_sp.reset(); + m_baton_is_command_baton = false; + m_callback_is_synchronous = false; + m_enabled = false; + m_condition_text.clear(); +} diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index d53e681514d..f4276e4e979 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -45,6 +45,203 @@ static void AddBreakpointDescription(Stream *s, Breakpoint *bp, s->EOL(); } +//------------------------------------------------------------------------- +// Modifiable Breakpoint Options +//------------------------------------------------------------------------- +#pragma mark Modify::CommandOptions +static OptionDefinition g_breakpoint_modify_options[] = { + // clang-format off + { LLDB_OPT_SET_1, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, + { LLDB_OPT_SET_1, false, "one-shot", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint is deleted the first time it stop causes a stop." }, + { LLDB_OPT_SET_1, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose index matches this argument." }, + { LLDB_OPT_SET_1, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." }, + { LLDB_OPT_SET_1, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this argument." }, + { LLDB_OPT_SET_1, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by this argument." }, + { LLDB_OPT_SET_1, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." }, + { LLDB_OPT_SET_1, false, "auto-continue",'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, + { LLDB_OPT_SET_2, false, "enable", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable the breakpoint." }, + { LLDB_OPT_SET_3, false, "disable", 'd', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disable the breakpoint." }, + { LLDB_OPT_SET_4, false, "command", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommand, "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." }, + // clang-format on +}; +class lldb_private::BreakpointOptionGroup : public OptionGroup +{ +public: + BreakpointOptionGroup() : + OptionGroup(), + m_bp_opts(false) {} + + ~BreakpointOptionGroup() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_breakpoint_modify_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = g_breakpoint_modify_options[option_idx].short_option; + + switch (short_option) { + case 'c': + // Normally an empty breakpoint condition marks is as unset. + // But we need to say it was passed in. + m_bp_opts.SetCondition(option_arg.str().c_str()); + m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition); + break; + case 'C': + m_commands.push_back(option_arg); + break; + case 'd': + m_bp_opts.SetEnabled(false); + break; + case 'e': + m_bp_opts.SetEnabled(true); + break; + case 'G': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_bp_opts.SetAutoContinue(value); + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -G option", + option_arg.str().c_str()); + } + break; + case 'i': + { + uint32_t ignore_count; + if (option_arg.getAsInteger(0, ignore_count)) + error.SetErrorStringWithFormat("invalid ignore count '%s'", + option_arg.str().c_str()); + else + m_bp_opts.SetIgnoreCount(ignore_count); + } + break; + case 'o': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_bp_opts.SetOneShot(value); + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -o option", + option_arg.str().c_str()); + } break; + case 't': + { + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID; + if (option_arg[0] != '\0') { + if (option_arg.getAsInteger(0, thread_id)) + error.SetErrorStringWithFormat("invalid thread id string '%s'", + option_arg.str().c_str()); + } + m_bp_opts.SetThreadID(thread_id); + } + break; + case 'T': + m_bp_opts.GetThreadSpec()->SetName(option_arg.str().c_str()); + break; + case 'q': + m_bp_opts.GetThreadSpec()->SetQueueName(option_arg.str().c_str()); + break; + case 'x': + { + uint32_t thread_index = UINT32_MAX; + if (option_arg[0] != '\n') { + if (option_arg.getAsInteger(0, thread_index)) + error.SetErrorStringWithFormat("invalid thread index string '%s'", + option_arg.str().c_str()); + } + m_bp_opts.GetThreadSpec()->SetIndex(thread_index); + } + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", + short_option); + break; + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_bp_opts.Clear(); + m_commands.clear(); + } + + Status OptionParsingFinished(ExecutionContext *execution_context) override { + if (!m_commands.empty()) + { + if (!m_commands.empty()) + { + auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>(); + + for (std::string &str : m_commands) + cmd_data->user_source.AppendString(str); + + cmd_data->stop_on_error = true; + m_bp_opts.SetCommandDataCallback(cmd_data); + } + } + return Status(); + } + + const BreakpointOptions &GetBreakpointOptions() + { + return m_bp_opts; + } + + std::vector<std::string> m_commands; + BreakpointOptions m_bp_opts; + +}; +static OptionDefinition g_breakpoint_dummy_options[] = { + // clang-format off + { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Act on Dummy breakpoints - i.e. breakpoints set before a file is provided, " + "which prime new targets." }, + // clang-format on +}; + +class BreakpointDummyOptionGroup : public OptionGroup +{ +public: + BreakpointDummyOptionGroup() : + OptionGroup() {} + + ~BreakpointDummyOptionGroup() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_breakpoint_dummy_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = g_breakpoint_modify_options[option_idx].short_option; + + switch (short_option) { + case 'D': + m_use_dummy = true; + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", + short_option); + break; + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_use_dummy = false; + } + + bool m_use_dummy; + +}; + // If an additional option set beyond LLDB_OPTION_SET_10 is added, make sure to // update the numbers passed to LLDB_OPT_SET_FROM_TO(...) appropriately. #define LLDB_OPT_FILE (LLDB_OPT_SET_FROM_TO(1, 9) & ~LLDB_OPT_SET_2) @@ -58,18 +255,7 @@ static OptionDefinition g_breakpoint_set_options[] = { // clang-format off { LLDB_OPT_NOT_10, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Set the breakpoint only in this shared library. Can repeat this option " "multiple times to specify multiple shared libraries." }, - { LLDB_OPT_SET_ALL, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, - { LLDB_OPT_SET_ALL, false, "one-shot", 'o', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "The breakpoint is deleted the first time it causes a stop." }, - { LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, - { LLDB_OPT_SET_ALL, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." }, - { LLDB_OPT_SET_ALL, false, "command", 'd', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommand, "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." }, - { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose indeX matches this argument." }, - { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." }, - { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this " - "argument." }, { LLDB_OPT_SET_ALL, false, "hardware", 'H', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Require the breakpoint to use hardware breakpoints." }, - { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by " - "this argument." }, { LLDB_OPT_FILE, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specifies the source file in which to set this breakpoint. Note, by default " "lldb only looks for files that are #included if they use the standard include " "file extensions. To set breakpoints on .c/.cpp/.m/.mm files that are " @@ -127,8 +313,6 @@ static OptionDefinition g_breakpoint_set_options[] = { "If not set the target.language setting is used." }, { LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "sKip the prologue if the breakpoint is at the beginning of a function. " "If not set the target.skip-prologue setting is used." }, - { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, " - "which prime new targets." }, { LLDB_OPT_SET_ALL, false, "breakpoint-name", 'N', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBreakpointName, "Adds this to the list of names for this breakpoint." }, { LLDB_OPT_OFFSET_APPLIES, false, "address-slide", 'R', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddress, "Add the specified offset to whatever address(es) the breakpoint resolves to. " "At present this applies the offset directly as given, and doesn't try to align it to instruction boundaries." }, @@ -158,24 +342,30 @@ public: interpreter, "breakpoint set", "Sets a breakpoint or set of breakpoints in the executable.", "breakpoint set <cmd-options>"), - m_options() {} + m_bp_opts(), m_options() { + // We're picking up all the normal options, commands and disable. + m_all_options.Append(&m_bp_opts, + LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4, + LLDB_OPT_SET_ALL); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } ~CommandObjectBreakpointSet() override = default; - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_all_options; } - class CommandOptions : public Options { + class CommandOptions : public OptionGroup { public: CommandOptions() - : Options(), m_condition(), m_filenames(), m_line_num(0), m_column(0), + : OptionGroup(), m_condition(), m_filenames(), m_line_num(0), m_column(0), m_func_names(), m_func_name_type_mask(eFunctionNameTypeNone), m_func_regexp(), m_source_text_regexp(), m_modules(), m_load_addr(), - m_ignore_count(0), m_thread_id(LLDB_INVALID_THREAD_ID), - m_thread_index(UINT32_MAX), m_thread_name(), m_queue_name(), m_catch_bp(false), m_throw_bp(true), m_hardware(false), m_exception_language(eLanguageTypeUnknown), m_language(lldb::eLanguageTypeUnknown), - m_skip_prologue(eLazyBoolCalculate), m_one_shot(false), + m_skip_prologue(eLazyBoolCalculate), m_all_files(false), m_move_to_nearest_code(eLazyBoolCalculate) {} ~CommandOptions() override = default; @@ -183,7 +373,7 @@ public: Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; - const int short_option = m_getopt_table[option_idx].val; + const int short_option = g_breakpoint_set_options[option_idx].short_option; switch (short_option) { case 'a': { @@ -206,18 +396,6 @@ public: option_arg.str().c_str()); break; - case 'c': - m_condition.assign(option_arg); - break; - - case 'd': - m_commands.push_back(option_arg); - break; - - case 'D': - m_use_dummy = true; - break; - case 'E': { LanguageType language = Language::GetLanguageTypeFromString(option_arg); @@ -262,15 +440,6 @@ public: m_func_name_type_mask |= eFunctionNameTypeFull; break; - case 'G' : { - bool success; - m_auto_continue = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat( - "Invalid boolean value for auto-continue option: '%s'", - option_arg.str().c_str()); - } break; - case 'h': { bool success; m_catch_bp = Args::StringToBoolean(option_arg, true, &success); @@ -284,12 +453,6 @@ public: m_hardware = true; break; - case 'i': - if (option_arg.getAsInteger(0, m_ignore_count)) - error.SetErrorStringWithFormat("invalid ignore count '%s'", - option_arg.str().c_str()); - break; - case 'K': { bool success; bool value; @@ -362,10 +525,6 @@ public: m_offset_addr = tmp_offset_addr; } break; - case 'o': - m_one_shot = true; - break; - case 'O': m_exception_extra_args.AppendArgument("-O"); m_exception_extra_args.AppendArgument(option_arg); @@ -375,10 +534,6 @@ public: m_source_text_regexp.assign(option_arg); break; - case 'q': - m_queue_name.assign(option_arg); - break; - case 'r': m_func_regexp.assign(option_arg); break; @@ -392,16 +547,6 @@ public: m_func_name_type_mask |= eFunctionNameTypeSelector; break; - case 't': - if (option_arg.getAsInteger(0, m_thread_id)) - error.SetErrorStringWithFormat("invalid thread id string '%s'", - option_arg.str().c_str()); - break; - - case 'T': - m_thread_name.assign(option_arg); - break; - case 'w': { bool success; m_throw_bp = Args::StringToBoolean(option_arg, true, &success); @@ -411,12 +556,6 @@ public: option_arg.str().c_str()); } break; - case 'x': - if (option_arg.getAsInteger(0, m_thread_index)) - error.SetErrorStringWithFormat("invalid thread index string '%s'", - option_arg.str().c_str()); - break; - case 'X': m_source_regex_func_names.insert(option_arg); break; @@ -431,7 +570,6 @@ public: } void OptionParsingStarting(ExecutionContext *execution_context) override { - m_condition.clear(); m_filenames.Clear(); m_line_num = 0; m_column = 0; @@ -442,26 +580,17 @@ public: m_modules.Clear(); m_load_addr = LLDB_INVALID_ADDRESS; m_offset_addr = 0; - m_ignore_count = 0; - m_thread_id = LLDB_INVALID_THREAD_ID; - m_thread_index = UINT32_MAX; - m_thread_name.clear(); - m_queue_name.clear(); m_catch_bp = false; m_throw_bp = true; m_hardware = false; m_exception_language = eLanguageTypeUnknown; m_language = lldb::eLanguageTypeUnknown; m_skip_prologue = eLazyBoolCalculate; - m_one_shot = false; - m_use_dummy = false; m_breakpoint_names.clear(); m_all_files = false; m_exception_extra_args.Clear(); m_move_to_nearest_code = eLazyBoolCalculate; m_source_regex_func_names.clear(); - m_commands.clear(); - m_auto_continue = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -482,30 +611,21 @@ public: FileSpecList m_modules; lldb::addr_t m_load_addr; lldb::addr_t m_offset_addr; - uint32_t m_ignore_count; - lldb::tid_t m_thread_id; - uint32_t m_thread_index; - std::string m_thread_name; - std::string m_queue_name; bool m_catch_bp; bool m_throw_bp; bool m_hardware; // Request to use hardware breakpoints lldb::LanguageType m_exception_language; lldb::LanguageType m_language; LazyBool m_skip_prologue; - bool m_one_shot; - bool m_use_dummy; bool m_all_files; Args m_exception_extra_args; LazyBool m_move_to_nearest_code; std::unordered_set<std::string> m_source_regex_func_names; - std::vector<std::string> m_commands; - bool m_auto_continue; }; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { - Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); + Target *target = GetSelectedOrDummyTarget(m_dummy_options.m_use_dummy); if (target == nullptr) { result.AppendError("Invalid target. Must set target before setting " @@ -540,7 +660,7 @@ protected: else if (m_options.m_exception_language != eLanguageTypeUnknown) break_type = eSetTypeException; - Breakpoint *bp = nullptr; + BreakpointSP bp_sp = nullptr; FileSpec module_spec; const bool internal = false; @@ -572,35 +692,32 @@ protected: // Only check for inline functions if LazyBool check_inlines = eLazyBoolCalculate; - bp = target - ->CreateBreakpoint(&(m_options.m_modules), file, - m_options.m_line_num, m_options.m_offset_addr, - check_inlines, m_options.m_skip_prologue, - internal, m_options.m_hardware, - m_options.m_move_to_nearest_code) - .get(); + bp_sp = target->CreateBreakpoint(&(m_options.m_modules), + file, + m_options.m_line_num, + m_options.m_offset_addr, + check_inlines, + m_options.m_skip_prologue, + internal, + m_options.m_hardware, + m_options.m_move_to_nearest_code); } break; case eSetTypeAddress: // Breakpoint by address { // If a shared library has been specified, make an lldb_private::Address - // with the library, and - // use that. That way the address breakpoint will track the load location - // of the library. + // with the library, and use that. That way the address breakpoint + // will track the load location of the library. size_t num_modules_specified = m_options.m_modules.GetSize(); if (num_modules_specified == 1) { const FileSpec *file_spec = m_options.m_modules.GetFileSpecPointerAtIndex(0); - bp = target - ->CreateAddressInModuleBreakpoint(m_options.m_load_addr, - internal, file_spec, - m_options.m_hardware) - .get(); + bp_sp = target->CreateAddressInModuleBreakpoint(m_options.m_load_addr, + internal, file_spec, + m_options.m_hardware); } else if (num_modules_specified == 0) { - bp = target - ->CreateBreakpoint(m_options.m_load_addr, internal, - m_options.m_hardware) - .get(); + bp_sp = target->CreateBreakpoint(m_options.m_load_addr, internal, + m_options.m_hardware); } else { result.AppendError("Only one shared library can be specified for " "address breakpoints."); @@ -616,13 +733,15 @@ protected: if (name_type_mask == 0) name_type_mask = eFunctionNameTypeAuto; - bp = target - ->CreateBreakpoint( - &(m_options.m_modules), &(m_options.m_filenames), - m_options.m_func_names, name_type_mask, m_options.m_language, - m_options.m_offset_addr, m_options.m_skip_prologue, internal, - m_options.m_hardware) - .get(); + bp_sp = target->CreateBreakpoint(&(m_options.m_modules), + &(m_options.m_filenames), + m_options.m_func_names, + name_type_mask, + m_options.m_language, + m_options.m_offset_addr, + m_options.m_skip_prologue, + internal, + m_options.m_hardware); } break; case eSetTypeFunctionRegexp: // Breakpoint by regular expression function @@ -639,12 +758,13 @@ protected: return false; } - bp = target - ->CreateFuncRegexBreakpoint( - &(m_options.m_modules), &(m_options.m_filenames), regexp, - m_options.m_language, m_options.m_skip_prologue, internal, - m_options.m_hardware) - .get(); + bp_sp = target->CreateFuncRegexBreakpoint(&(m_options.m_modules), + &(m_options.m_filenames), + regexp, + m_options.m_language, + m_options.m_skip_prologue, + internal, + m_options.m_hardware); } break; case eSetTypeSourceRegexp: // Breakpoint by regexp on source text. @@ -673,26 +793,30 @@ protected: result.SetStatus(eReturnStatusFailed); return false; } - bp = target - ->CreateSourceRegexBreakpoint( - &(m_options.m_modules), &(m_options.m_filenames), - m_options.m_source_regex_func_names, regexp, internal, - m_options.m_hardware, m_options.m_move_to_nearest_code) - .get(); + bp_sp = + target->CreateSourceRegexBreakpoint(&(m_options.m_modules), + &(m_options.m_filenames), + m_options + .m_source_regex_func_names, + regexp, + internal, + m_options.m_hardware, + m_options.m_move_to_nearest_code); } break; case eSetTypeException: { Status precond_error; - bp = target - ->CreateExceptionBreakpoint( - m_options.m_exception_language, m_options.m_catch_bp, - m_options.m_throw_bp, internal, - &m_options.m_exception_extra_args, &precond_error) - .get(); + bp_sp = target->CreateExceptionBreakpoint(m_options.m_exception_language, + m_options.m_catch_bp, + m_options.m_throw_bp, + internal, + &m_options + .m_exception_extra_args, + &precond_error); if (precond_error.Fail()) { result.AppendErrorWithFormat( "Error setting extra exception arguments: %s", precond_error.AsCString()); - target->RemoveBreakpointByID(bp->GetID()); + target->RemoveBreakpointByID(bp_sp->GetID()); result.SetStatus(eReturnStatusFailed); return false; } @@ -702,76 +826,43 @@ protected: } // Now set the various options that were passed in: - if (bp) { - if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) - bp->SetThreadID(m_options.m_thread_id); - - if (m_options.m_thread_index != UINT32_MAX) - bp->GetOptions()->GetThreadSpec()->SetIndex(m_options.m_thread_index); - - if (!m_options.m_thread_name.empty()) - bp->GetOptions()->GetThreadSpec()->SetName( - m_options.m_thread_name.c_str()); - - if (!m_options.m_queue_name.empty()) - bp->GetOptions()->GetThreadSpec()->SetQueueName( - m_options.m_queue_name.c_str()); - - if (m_options.m_ignore_count != 0) - bp->GetOptions()->SetIgnoreCount(m_options.m_ignore_count); - - if (!m_options.m_condition.empty()) - bp->GetOptions()->SetCondition(m_options.m_condition.c_str()); + if (bp_sp) { + bp_sp->GetOptions()->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions()); if (!m_options.m_breakpoint_names.empty()) { Status name_error; for (auto name : m_options.m_breakpoint_names) { - bp->AddName(name.c_str(), name_error); + target->AddNameToBreakpoint(bp_sp, name.c_str(), name_error); if (name_error.Fail()) { result.AppendErrorWithFormat("Invalid breakpoint name: %s", name.c_str()); - target->RemoveBreakpointByID(bp->GetID()); + target->RemoveBreakpointByID(bp_sp->GetID()); result.SetStatus(eReturnStatusFailed); return false; } } } - - bp->SetOneShot(m_options.m_one_shot); - bp->SetAutoContinue(m_options.m_auto_continue); - - if (!m_options.m_commands.empty()) - { - auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>(); - - for (std::string &str : m_options.m_commands) - cmd_data->user_source.AppendString(str); - - cmd_data->stop_on_error = true; - bp->GetOptions()->SetCommandDataCallback(cmd_data); - } } - - if (bp) { + + if (bp_sp) { Stream &output_stream = result.GetOutputStream(); const bool show_locations = false; - bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, show_locations); if (target == m_interpreter.GetDebugger().GetDummyTarget()) output_stream.Printf("Breakpoint set in dummy target, will get copied " "into future targets.\n"); else { // Don't print out this warning for exception breakpoints. They can get - // set before the target - // is set, but we won't know how to actually set the breakpoint till we - // run. - if (bp->GetNumLocations() == 0 && break_type != eSetTypeException) { + // set before the target is set, but we won't know how to actually set + // the breakpoint till we run. + if (bp_sp->GetNumLocations() == 0 && break_type != eSetTypeException) { output_stream.Printf("WARNING: Unable to resolve breakpoint to any " "actual locations.\n"); } } result.SetStatus(eReturnStatusSuccessFinishResult); - } else if (!bp) { + } else if (!bp_sp) { result.AppendError("Breakpoint creation failed: No breakpoint created."); result.SetStatus(eReturnStatusFailed); } @@ -813,30 +904,15 @@ private: return true; } + BreakpointOptionGroup m_bp_opts; + BreakpointDummyOptionGroup m_dummy_options; CommandOptions m_options; + OptionGroupOptions m_all_options; }; //------------------------------------------------------------------------- // CommandObjectBreakpointModify //------------------------------------------------------------------------- - -#pragma mark Modify::CommandOptions -static OptionDefinition g_breakpoint_modify_options[] = { - // clang-format off - { LLDB_OPT_SET_ALL, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, - { LLDB_OPT_SET_ALL, false, "one-shot", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint is deleted the first time it stop causes a stop." }, - { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose index matches this argument." }, - { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." }, - { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this argument." }, - { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by this argument." }, - { LLDB_OPT_SET_ALL, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." }, - { LLDB_OPT_SET_1, false, "enable", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable the breakpoint." }, - { LLDB_OPT_SET_2, false, "disable", 'd', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disable the breakpoint." }, - { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." }, - { LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, - // clang-format on -}; - #pragma mark Modify class CommandObjectBreakpointModify : public CommandObjectParsed { @@ -857,163 +933,21 @@ public: // Add the entry for the first argument for this command to the object's // arguments vector. m_arguments.push_back(arg); + + m_options.Append(&m_bp_opts, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, + LLDB_OPT_SET_ALL); + m_options.Append(&m_dummy_opts, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); + m_options.Finalize(); } ~CommandObjectBreakpointModify() override = default; Options *GetOptions() override { return &m_options; } - class CommandOptions : public Options { - public: - CommandOptions() - : Options(), m_ignore_count(0), m_thread_id(LLDB_INVALID_THREAD_ID), - m_thread_id_passed(false), m_thread_index(UINT32_MAX), - m_thread_index_passed(false), m_thread_name(), m_queue_name(), - m_condition(), m_one_shot(false), m_enable_passed(false), - m_enable_value(false), m_name_passed(false), m_queue_passed(false), - m_condition_passed(false), m_one_shot_passed(false), - m_use_dummy(false) {} - - ~CommandOptions() override = default; - - Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, - ExecutionContext *execution_context) override { - Status error; - const int short_option = m_getopt_table[option_idx].val; - - switch (short_option) { - case 'c': - m_condition = option_arg; - m_condition_passed = true; - break; - case 'd': - m_enable_passed = true; - m_enable_value = false; - break; - case 'D': - m_use_dummy = true; - break; - case 'e': - m_enable_passed = true; - m_enable_value = true; - break; - case 'G': { - bool value, success; - value = Args::StringToBoolean(option_arg, false, &success); - if (success) { - m_auto_continue_passed = true; - m_auto_continue = value; - } else - error.SetErrorStringWithFormat( - "invalid boolean value '%s' passed for -G option", - option_arg.str().c_str()); - } break; - case 'i': - if (option_arg.getAsInteger(0, m_ignore_count)) - error.SetErrorStringWithFormat("invalid ignore count '%s'", - option_arg.str().c_str()); - break; - case 'o': { - bool value, success; - value = Args::StringToBoolean(option_arg, false, &success); - if (success) { - m_one_shot_passed = true; - m_one_shot = value; - } else - error.SetErrorStringWithFormat( - "invalid boolean value '%s' passed for -o option", - option_arg.str().c_str()); - } break; - case 't': - if (option_arg[0] == '\0') { - m_thread_id = LLDB_INVALID_THREAD_ID; - m_thread_id_passed = true; - } else { - if (option_arg.getAsInteger(0, m_thread_id)) - error.SetErrorStringWithFormat("invalid thread id string '%s'", - option_arg.str().c_str()); - else - m_thread_id_passed = true; - } - break; - case 'T': - m_thread_name = option_arg; - m_name_passed = true; - break; - case 'q': - m_queue_name = option_arg; - m_queue_passed = true; - break; - case 'x': - if (option_arg[0] == '\n') { - m_thread_index = UINT32_MAX; - m_thread_index_passed = true; - } else { - if (option_arg.getAsInteger(0, m_thread_index)) - error.SetErrorStringWithFormat("invalid thread index string '%s'", - option_arg.str().c_str()); - else - m_thread_index_passed = true; - } - break; - default: - error.SetErrorStringWithFormat("unrecognized option '%c'", - short_option); - break; - } - - return error; - } - - void OptionParsingStarting(ExecutionContext *execution_context) override { - m_ignore_count = 0; - m_thread_id = LLDB_INVALID_THREAD_ID; - m_thread_id_passed = false; - m_thread_index = UINT32_MAX; - m_thread_index_passed = false; - m_thread_name.clear(); - m_queue_name.clear(); - m_condition.clear(); - m_one_shot = false; - m_enable_passed = false; - m_queue_passed = false; - m_name_passed = false; - m_condition_passed = false; - m_one_shot_passed = false; - m_use_dummy = false; - m_auto_continue = false; - m_auto_continue_passed = false; - } - - llvm::ArrayRef<OptionDefinition> GetDefinitions() override { - return llvm::makeArrayRef(g_breakpoint_modify_options); - } - - // Instance variables to hold the values for command options. - - uint32_t m_ignore_count; - lldb::tid_t m_thread_id; - bool m_thread_id_passed; - uint32_t m_thread_index; - bool m_thread_index_passed; - std::string m_thread_name; - std::string m_queue_name; - std::string m_condition; - bool m_one_shot; - bool m_enable_passed; - bool m_enable_value; - bool m_name_passed; - bool m_queue_passed; - bool m_condition_passed; - bool m_one_shot_passed; - bool m_use_dummy; - bool m_auto_continue; - bool m_auto_continue_passed; - }; - protected: bool DoExecute(Args &command, CommandReturnObject &result) override { - Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); + Target *target = GetSelectedOrDummyTarget(m_dummy_opts.m_use_dummy); if (target == nullptr) { result.AppendError("Invalid target. No existing target or breakpoints."); result.SetStatus(eReturnStatusFailed); @@ -1026,7 +960,8 @@ protected: BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { const size_t count = valid_bp_ids.GetSize(); @@ -1039,55 +974,12 @@ protected: if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) { BreakpointLocation *location = bp->FindLocationByID(cur_bp_id.GetLocationID()).get(); - if (location) { - if (m_options.m_thread_id_passed) - location->SetThreadID(m_options.m_thread_id); - - if (m_options.m_thread_index_passed) - location->SetThreadIndex(m_options.m_thread_index); - - if (m_options.m_name_passed) - location->SetThreadName(m_options.m_thread_name.c_str()); - - if (m_options.m_queue_passed) - location->SetQueueName(m_options.m_queue_name.c_str()); - - if (m_options.m_ignore_count != 0) - location->SetIgnoreCount(m_options.m_ignore_count); - - if (m_options.m_enable_passed) - location->SetEnabled(m_options.m_enable_value); - - if (m_options.m_condition_passed) - location->SetCondition(m_options.m_condition.c_str()); - - if (m_options.m_auto_continue_passed) - location->SetAutoContinue(m_options.m_auto_continue); - } + if (location) + location->GetLocationOptions() + ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions()); } else { - if (m_options.m_thread_id_passed) - bp->SetThreadID(m_options.m_thread_id); - - if (m_options.m_thread_index_passed) - bp->SetThreadIndex(m_options.m_thread_index); - - if (m_options.m_name_passed) - bp->SetThreadName(m_options.m_thread_name.c_str()); - - if (m_options.m_queue_passed) - bp->SetQueueName(m_options.m_queue_name.c_str()); - - if (m_options.m_ignore_count != 0) - bp->SetIgnoreCount(m_options.m_ignore_count); - - if (m_options.m_enable_passed) - bp->SetEnabled(m_options.m_enable_value); - - if (m_options.m_condition_passed) - bp->SetCondition(m_options.m_condition.c_str()); - - if (m_options.m_auto_continue_passed) - bp->SetAutoContinue(m_options.m_auto_continue); + bp->GetOptions() + ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions()); } } } @@ -1097,7 +989,9 @@ protected: } private: - CommandOptions m_options; + BreakpointOptionGroup m_bp_opts; + BreakpointDummyOptionGroup m_dummy_opts; + OptionGroupOptions m_options; }; //------------------------------------------------------------------------- @@ -1146,7 +1040,7 @@ protected: if (command.empty()) { // No breakpoint selected; enable all currently set breakpoints. - target->EnableAllBreakpoints(); + target->EnableAllowedBreakpoints(); result.AppendMessageWithFormat("All breakpoints enabled. (%" PRIu64 " breakpoints)\n", (uint64_t)num_breakpoints); @@ -1155,7 +1049,8 @@ protected: // Particular breakpoint selected; enable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { int enable_count = 0; @@ -1259,7 +1154,7 @@ protected: if (command.empty()) { // No breakpoint selected; disable all currently set breakpoints. - target->DisableAllBreakpoints(); + target->DisableAllowedBreakpoints(); result.AppendMessageWithFormat("All breakpoints disabled. (%" PRIu64 " breakpoints)\n", (uint64_t)num_breakpoints); @@ -1269,7 +1164,8 @@ protected: BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { int disable_count = 0; @@ -1436,14 +1332,17 @@ protected: result.AppendMessage("Current breakpoints:"); for (size_t i = 0; i < num_breakpoints; ++i) { Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(i).get(); - AddBreakpointDescription(&output_stream, breakpoint, m_options.m_level); + if (breakpoint->AllowList()) + AddBreakpointDescription(&output_stream, breakpoint, + m_options.m_level); } result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { // Particular breakpoints selected; show info about that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) { @@ -1731,7 +1630,7 @@ protected: true)) { result.AppendMessage("Operation cancelled..."); } else { - target->RemoveAllBreakpoints(); + target->RemoveAllowedBreakpoints(); result.AppendMessageWithFormat( "All breakpoints removed. (%" PRIu64 " breakpoint%s)\n", (uint64_t)num_breakpoints, num_breakpoints > 1 ? "s" : ""); @@ -1741,7 +1640,8 @@ protected: // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); if (result.Succeeded()) { int delete_count = 0; @@ -1789,7 +1689,7 @@ static OptionDefinition g_breakpoint_name_options[] = { // clang-format off {LLDB_OPT_SET_1, false, "name", 'N', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBreakpointName, "Specifies a breakpoint name to use."}, {LLDB_OPT_SET_2, false, "breakpoint-id", 'B', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBreakpointID, "Specify a breakpoint ID to use."}, - {LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Operate on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + {LLDB_OPT_SET_3, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Operate on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, // clang-format on }; class BreakpointNameOptionGroup : public OptionGroup { @@ -1849,6 +1749,188 @@ public: OptionValueBoolean m_use_dummy; }; +static OptionDefinition g_breakpoint_access_options[] = { + // clang-format off + {LLDB_OPT_SET_1, false, "allow-list", 'L', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Determines whether the breakpoint will show up in break list if not referred to explicitly."}, + {LLDB_OPT_SET_2, false, "allow-disable", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Determines whether the breakpoint can be disabled by name or when all breakpoints are disabled."}, + {LLDB_OPT_SET_3, false, "allow-delete", 'D', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Determines whether the breakpoint can be deleted by name or when all breakpoints are deleted."}, + // clang-format on +}; + +class BreakpointAccessOptionGroup : public OptionGroup +{ +public: + BreakpointAccessOptionGroup() : + OptionGroup() + {} + + ~BreakpointAccessOptionGroup() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_breakpoint_access_options); + } + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option + = g_breakpoint_access_options[option_idx].short_option; + + switch (short_option) { + case 'L': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_permissions.SetAllowList(value); + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -L option", + option_arg.str().c_str()); + } break; + case 'A': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_permissions.SetAllowDisable(value); + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -L option", + option_arg.str().c_str()); + } break; + case 'D': { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) { + m_permissions.SetAllowDelete(value); + } else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -L option", + option_arg.str().c_str()); + } break; + + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + } + + const BreakpointName::Permissions &GetPermissions() const + { + return m_permissions; + } + BreakpointName::Permissions m_permissions; +}; + +class CommandObjectBreakpointNameConfigure : public CommandObjectParsed { +public: + CommandObjectBreakpointNameConfigure(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "configure", "Configure the options for the breakpoint" + " name provided. " + "If you provide a breakpoint id, the options will be copied from " + "the breakpoint, otherwise only the options specified will be set " + "on the name.", + "breakpoint name configure <command-options> " + "<breakpoint-name-list>"), + m_bp_opts(), m_option_group() { + // Create the first variant for the first (and only) argument for this + // command. + CommandArgumentEntry arg1; + CommandArgumentData id_arg; + id_arg.arg_type = eArgTypeBreakpointName; + id_arg.arg_repetition = eArgRepeatOptional; + arg1.push_back(id_arg); + m_arguments.push_back(arg1); + + m_option_group.Append(&m_bp_opts, + LLDB_OPT_SET_ALL, + LLDB_OPT_SET_1); + m_option_group.Append(&m_access_options, + LLDB_OPT_SET_ALL, + LLDB_OPT_SET_ALL); + m_option_group.Append(&m_bp_id, LLDB_OPT_SET_2, LLDB_OPT_SET_2); + m_option_group.Finalize(); + } + + ~CommandObjectBreakpointNameConfigure() override = default; + + Options *GetOptions() override { return &m_option_group; } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + + const size_t argc = command.GetArgumentCount(); + if (argc == 0) { + result.AppendError("No names provided."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Target *target = + GetSelectedOrDummyTarget(false); + + if (target == nullptr) { + result.AppendError("Invalid target. No existing target or breakpoints."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + std::unique_lock<std::recursive_mutex> lock; + target->GetBreakpointList().GetListMutex(lock); + + // Make a pass through first to see that all the names are legal. + for (auto &entry : command.entries()) { + Status error; + if (!BreakpointID::StringIsBreakpointName(entry.ref, error)) + { + result.AppendErrorWithFormat("Invalid breakpoint name: %s - %s", + entry.c_str(), error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + // Now configure them, we already pre-checked the names so we don't need + // to check the error: + BreakpointSP bp_sp; + if (m_bp_id.m_breakpoint.OptionWasSet()) + { + lldb::break_id_t bp_id = m_bp_id.m_breakpoint.GetUInt64Value(); + bp_sp = target->GetBreakpointByID(bp_id); + if (!bp_sp) + { + result.AppendErrorWithFormatv("Could not find specified breakpoint {0}", + bp_id); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + Status error; + for (auto &entry : command.entries()) { + ConstString name(entry.c_str()); + BreakpointName *bp_name = target->FindBreakpointName(name, true, error); + if (!bp_name) + continue; + if (bp_sp) + target->ConfigureBreakpointName(*bp_name, + *bp_sp->GetOptions(), + m_access_options.GetPermissions()); + else + target->ConfigureBreakpointName(*bp_name, + m_bp_opts.GetBreakpointOptions(), + m_access_options.GetPermissions()); + } + return true; + } + +private: + BreakpointNameOptionGroup m_bp_id; // Only using the id part of this. + BreakpointOptionGroup m_bp_opts; + BreakpointAccessOptionGroup m_access_options; + OptionGroupOptions m_option_group; +}; + class CommandObjectBreakpointNameAdd : public CommandObjectParsed { public: CommandObjectBreakpointNameAdd(CommandInterpreter &interpreter) @@ -1904,7 +1986,8 @@ protected: // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { if (valid_bp_ids.GetSize() == 0) { @@ -1913,13 +1996,14 @@ protected: return false; } size_t num_valid_ids = valid_bp_ids.GetSize(); + const char *bp_name = m_name_options.m_name.GetCurrentValue(); + Status error; // This error reports illegal names, but we've already + // checked that, so we don't need to check it again here. for (size_t index = 0; index < num_valid_ids; index++) { lldb::break_id_t bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id); - Status error; // We don't need to check the error here, since the option - // parser checked it... - bp_sp->AddName(m_name_options.m_name.GetCurrentValue(), error); + target->AddNameToBreakpoint(bp_sp, bp_name, error); } } @@ -1987,7 +2071,8 @@ protected: // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); if (result.Succeeded()) { if (valid_bp_ids.GetSize() == 0) { @@ -1995,12 +2080,13 @@ protected: result.SetStatus(eReturnStatusFailed); return false; } + ConstString bp_name(m_name_options.m_name.GetCurrentValue()); size_t num_valid_ids = valid_bp_ids.GetSize(); for (size_t index = 0; index < num_valid_ids; index++) { lldb::break_id_t bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id); - bp_sp->RemoveName(m_name_options.m_name.GetCurrentValue()); + target->RemoveNameFromBreakpoint(bp_sp, bp_name); } } @@ -2016,11 +2102,12 @@ class CommandObjectBreakpointNameList : public CommandObjectParsed { public: CommandObjectBreakpointNameList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "list", - "List either the names for a breakpoint or the " - "breakpoints for a given name.", + "List either the names for a breakpoint or info " + "about a given name. With no arguments, lists all " + "names", "breakpoint name list <command-options>"), m_name_options(), m_option_group() { - m_option_group.Append(&m_name_options); + m_option_group.Append(&m_name_options, LLDB_OPT_SET_3, LLDB_OPT_SET_ALL); m_option_group.Finalize(); } @@ -2038,42 +2125,57 @@ protected: result.SetStatus(eReturnStatusFailed); return false; } - - if (m_name_options.m_name.OptionWasSet()) { - const char *name = m_name_options.m_name.GetCurrentValue(); - std::unique_lock<std::recursive_mutex> lock; - target->GetBreakpointList().GetListMutex(lock); - - BreakpointList &breakpoints = target->GetBreakpointList(); - for (BreakpointSP bp_sp : breakpoints.Breakpoints()) { - if (bp_sp->MatchesName(name)) { + + + std::vector<std::string> name_list; + if (command.empty()) { + target->GetBreakpointNames(name_list); + } else { + for (const Args::ArgEntry &arg : command) + { + name_list.push_back(arg.c_str()); + } + } + + if (name_list.empty()) { + result.AppendMessage("No breakpoint names found."); + } else { + for (const std::string &name_str : name_list) { + const char *name = name_str.c_str(); + // First print out the options for the name: + Status error; + BreakpointName *bp_name = target->FindBreakpointName(ConstString(name), + false, + error); + if (bp_name) + { StreamString s; - bp_sp->GetDescription(&s, eDescriptionLevelBrief); - s.EOL(); - result.AppendMessage(s.GetString()); + result.AppendMessageWithFormat("Name: %s\n", name); + if (bp_name->GetDescription(&s, eDescriptionLevelFull)) + { + result.AppendMessage(s.GetString()); + } + + std::unique_lock<std::recursive_mutex> lock; + target->GetBreakpointList().GetListMutex(lock); + + BreakpointList &breakpoints = target->GetBreakpointList(); + bool any_set = false; + for (BreakpointSP bp_sp : breakpoints.Breakpoints()) { + if (bp_sp->MatchesName(name)) { + StreamString s; + any_set = true; + bp_sp->GetDescription(&s, eDescriptionLevelBrief); + s.EOL(); + result.AppendMessage(s.GetString()); + } + } + if (!any_set) + result.AppendMessage("No breakpoints using this name."); + } else { + result.AppendMessageWithFormat("Name: %s not found.\n", name); } } - - } else if (m_name_options.m_breakpoint.OptionWasSet()) { - BreakpointSP bp_sp = target->GetBreakpointList().FindBreakpointByID( - m_name_options.m_breakpoint.GetCurrentValue()); - if (bp_sp) { - std::vector<std::string> names; - bp_sp->GetNames(names); - result.AppendMessage("Names:"); - for (auto name : names) - result.AppendMessageWithFormat(" %s\n", name.c_str()); - } else { - result.AppendErrorWithFormat( - "Could not find breakpoint %" PRId64 ".\n", - m_name_options.m_breakpoint.GetCurrentValue()); - result.SetStatus(eReturnStatusFailed); - return false; - } - } else { - result.SetError("Must specify -N or -B option to list."); - result.SetStatus(eReturnStatusFailed); - return false; } return true; } @@ -2098,10 +2200,13 @@ public: new CommandObjectBreakpointNameDelete(interpreter)); CommandObjectSP list_command_object( new CommandObjectBreakpointNameList(interpreter)); + CommandObjectSP configure_command_object( + new CommandObjectBreakpointNameConfigure(interpreter)); LoadSubCommand("add", add_command_object); LoadSubCommand("delete", delete_command_object); LoadSubCommand("list", list_command_object); + LoadSubCommand("configure", configure_command_object); } ~CommandObjectBreakpointName() override = default; @@ -2327,7 +2432,8 @@ protected: BreakpointIDList valid_bp_ids; if (!command.empty()) { CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); if (!result.Succeeded()) { result.SetStatus(eReturnStatusFailed); @@ -2412,7 +2518,10 @@ CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default; void CommandObjectMultiwordBreakpoint::VerifyIDs(Args &args, Target *target, bool allow_locations, CommandReturnObject &result, - BreakpointIDList *valid_ids) { + BreakpointIDList *valid_ids, + BreakpointName::Permissions + ::PermissionKinds + purpose) { // args can be strings representing 1). integers (for breakpoint ids) // 2). the full breakpoint & location // canonical representation @@ -2446,8 +2555,8 @@ void CommandObjectMultiwordBreakpoint::VerifyIDs(Args &args, Target *target, // all the breakpoint ids in the range, and shove all of those breakpoint id // strings into TEMP_ARGS. - BreakpointIDList::FindAndReplaceIDRanges(args, target, allow_locations, - result, temp_args); + BreakpointIDList::FindAndReplaceIDRanges(args, target, allow_locations, + purpose, result, temp_args); // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual // BreakpointIDList: diff --git a/lldb/source/Commands/CommandObjectBreakpoint.h b/lldb/source/Commands/CommandObjectBreakpoint.h index 6e14b8f876a..5e1026a6b7e 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.h +++ b/lldb/source/Commands/CommandObjectBreakpoint.h @@ -18,11 +18,14 @@ // Other libraries and framework includes // Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointName.h" #include "lldb/Core/Address.h" #include "lldb/Core/STLUtils.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/Options.h" + namespace lldb_private { //------------------------------------------------------------------------- @@ -37,20 +40,26 @@ public: static void VerifyBreakpointOrLocationIDs(Args &args, Target *target, CommandReturnObject &result, - BreakpointIDList *valid_ids) { - VerifyIDs(args, target, true, result, valid_ids); + BreakpointIDList *valid_ids, + BreakpointName::Permissions + ::PermissionKinds purpose) { + VerifyIDs(args, target, true, result, valid_ids, purpose); } static void VerifyBreakpointIDs(Args &args, Target *target, CommandReturnObject &result, - BreakpointIDList *valid_ids) { - VerifyIDs(args, target, false, result, valid_ids); + BreakpointIDList *valid_ids, + BreakpointName::Permissions::PermissionKinds + purpose) { + VerifyIDs(args, target, false, result, valid_ids, purpose); } private: static void VerifyIDs(Args &args, Target *target, bool allow_locations, CommandReturnObject &result, - BreakpointIDList *valid_ids); + BreakpointIDList *valid_ids, + BreakpointName::Permissions::PermissionKinds + purpose); }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index ecb00f9a339..170cb851311 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -390,7 +390,8 @@ protected: BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); m_bp_options_vec.clear(); @@ -571,7 +572,8 @@ protected: BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { const size_t count = valid_bp_ids.GetSize(); @@ -662,7 +664,8 @@ protected: BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids); + command, target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { const size_t count = valid_bp_ids.GetSize(); diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 96c245cb8bb..b1937d8b17b 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -1113,7 +1113,8 @@ CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = { { eArgTypeWatchpointID, "watchpt-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Watchpoint IDs are positive integers." }, { eArgTypeWatchpointIDRange, "watchpt-id-list", CommandCompletions::eNoCompletion, { nullptr, false }, "For example, '1-3' or '1 to 3'." }, { eArgTypeWatchType, "watch-type", CommandCompletions::eNoCompletion, { nullptr, false }, "Specify the type for a watchpoint." }, - { eArgRawInput, "raw-input", CommandCompletions::eNoCompletion, { nullptr, false }, "Free-form text passed to a command without prior interpretation, allowing spaces without requiring quotes. To pass arguments and free form text put two dashes ' -- ' between the last argument and any raw input." } + { eArgRawInput, "raw-input", CommandCompletions::eNoCompletion, { nullptr, false }, "Free-form text passed to a command without prior interpretation, allowing spaces without requiring quotes. To pass arguments and free form text put two dashes ' -- ' between the last argument and any raw input." }, + { eArgTypeCommand, "command", CommandCompletions::eNoCompletion, { nullptr, false }, "An LLDB Command line command." } // clang-format on }; diff --git a/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp index 7e46afcccda..ad1083be028 100644 --- a/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -3615,13 +3615,15 @@ RenderScriptRuntime::CreateKernelBreakpoint(const ConstString &name) { } BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name)); - BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint( + Target &target = GetProcess()->GetTarget(); + BreakpointSP bp = target.CreateBreakpoint( m_filtersp, resolver_sp, false, false, false); // Give RS breakpoints a specific name, so the user can manipulate them as a // group. Status err; - if (!bp->AddName("RenderScriptKernel", err)) + target.AddNameToBreakpoint(bp, "RenderScriptKernel", err); + if (err.Fail() && log) if (log) log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, err.AsCString()); @@ -3643,14 +3645,15 @@ RenderScriptRuntime::CreateReductionBreakpoint(const ConstString &name, BreakpointResolverSP resolver_sp(new RSReduceBreakpointResolver( nullptr, name, &m_rsmodules, kernel_types)); - BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint( + Target &target = GetProcess()->GetTarget(); + BreakpointSP bp = target.CreateBreakpoint( m_filtersp, resolver_sp, false, false, false); // Give RS breakpoints a specific name, so the user can manipulate them as a // group. Status err; - if (!bp->AddName("RenderScriptReduction", err)) - if (log) + target.AddNameToBreakpoint(bp, "RenderScriptReduction", err); + if (err.Fail() && log) log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, err.AsCString()); @@ -3885,15 +3888,16 @@ RenderScriptRuntime::CreateScriptGroupBreakpoint(const ConstString &name, BreakpointResolverSP resolver_sp(new RSScriptGroupBreakpointResolver( nullptr, name, m_scriptGroups, stop_on_all)); - BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint( + Target &target = GetProcess()->GetTarget(); + BreakpointSP bp = target.CreateBreakpoint( m_filtersp, resolver_sp, false, false, false); // Give RS breakpoints a specific name, so the user can manipulate them as a // group. Status err; - if (!bp->AddName(name.AsCString(), err)) - if (log) - log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, - err.AsCString()); + target.AddNameToBreakpoint(bp, name.GetCString(), err); + if (err.Fail() && log) + log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, + err.AsCString()); // ask the breakpoint to resolve itself bp->ResolveBreakpoint(); return bp; diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index d97f651ca08..08546d57c2a 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -123,6 +123,13 @@ void Target::PrimeFromDummyTarget(Target *target) { BreakpointSP new_bp(new Breakpoint(*this, *breakpoint_sp.get())); AddBreakpoint(new_bp, false); } + + for (auto bp_name_entry : target->m_breakpoint_names) + { + + BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second); + AddBreakpointName(new_bp_name); + } } void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) { @@ -601,6 +608,112 @@ void Target::AddBreakpoint(lldb::BreakpointSP bp_sp, bool internal) { } } +void Target::AddNameToBreakpoint(BreakpointID &id, + const char *name, + Status &error) + { + BreakpointSP bp_sp + = m_breakpoint_list.FindBreakpointByID(id.GetBreakpointID()); + if (!bp_sp) + { + StreamString s; + id.GetDescription(&s, eDescriptionLevelBrief); + error.SetErrorStringWithFormat("Could not find breakpoint %s", + s.GetData()); + return; + } + AddNameToBreakpoint(bp_sp, name, error); + } + +void Target::AddNameToBreakpoint(BreakpointSP &bp_sp, + const char *name, + Status &error) + { + if (!bp_sp) + return; + + BreakpointName *bp_name = FindBreakpointName(ConstString(name), true, error); + if (!bp_name) + return; + + bp_name->ConfigureBreakpoint(bp_sp); + bp_sp->AddName(name); + } + +void Target::AddBreakpointName(BreakpointName *bp_name) { + m_breakpoint_names.insert(std::make_pair(bp_name->GetName(), bp_name)); +} + +BreakpointName *Target::FindBreakpointName(const ConstString &name, + bool can_create, + Status &error) +{ + BreakpointID::StringIsBreakpointName(name.GetStringRef(), error); + if (!error.Success()) + return nullptr; + + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + if (iter == m_breakpoint_names.end()) { + if (!can_create) + { + error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and " + "can_create is false.", name.AsCString()); + return nullptr; + } + + iter = m_breakpoint_names.insert(std::make_pair(name, + new BreakpointName(name))) + .first; + } + return (iter->second); +} + +void +Target::DeleteBreakpointName(const ConstString &name) +{ + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + + if (iter != m_breakpoint_names.end()) { + const char *name_cstr = name.AsCString(); + m_breakpoint_names.erase(iter); + for (auto bp_sp : m_breakpoint_list.Breakpoints()) + bp_sp->RemoveName(name_cstr); + } +} + +void Target::RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp, + const ConstString &name) +{ + bp_sp->RemoveName(name.AsCString()); +} + +void Target::ConfigureBreakpointName(BreakpointName &bp_name, + const BreakpointOptions &new_options, + const BreakpointName::Permissions &new_permissions) +{ + bp_name.GetOptions().CopyOverSetOptions(new_options); + bp_name.GetPermissions().MergeInto(new_permissions); + ApplyNameToBreakpoints(bp_name); +} + +void Target::ApplyNameToBreakpoints(BreakpointName &bp_name) { + BreakpointList bkpts_with_name(false); + m_breakpoint_list.FindBreakpointsByName(bp_name.GetName().AsCString(), + bkpts_with_name); + + for (auto bp_sp : bkpts_with_name.Breakpoints()) + bp_name.ConfigureBreakpoint(bp_sp); +} + +void Target::GetBreakpointNames(std::vector<std::string> &names) +{ + names.clear(); + for (auto bp_name : m_breakpoint_names) { + names.push_back(bp_name.first.AsCString()); + } + std::sort(names.begin(), names.end()); +} + bool Target::ProcessIsValid() { return (m_process_sp && m_process_sp->IsAlive()); } @@ -703,6 +816,17 @@ WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, return wp_sp; } +void Target::RemoveAllowedBreakpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("Target::%s \n", __FUNCTION__); + + m_breakpoint_list.RemoveAllowed(true); + + m_last_created_breakpoint.reset(); +} + void Target::RemoveAllBreakpoints(bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) @@ -727,6 +851,14 @@ void Target::DisableAllBreakpoints(bool internal_also) { m_internal_breakpoint_list.SetEnabledAll(false); } +void Target::DisableAllowedBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("Target::%s", __FUNCTION__); + + m_breakpoint_list.SetEnabledAllowed(false); +} + void Target::EnableAllBreakpoints(bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) @@ -738,6 +870,14 @@ void Target::EnableAllBreakpoints(bool internal_also) { m_internal_breakpoint_list.SetEnabledAll(true); } +void Target::EnableAllowedBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("Target::%s", __FUNCTION__); + + m_breakpoint_list.SetEnabledAllowed(true); +} + bool Target::RemoveBreakpointByID(break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) |

