Add SB API's for writing breakpoints to & creating the from a file.

Moved the guts of the code from CommandObjectBreakpoint to Target (should
have done it that way in the first place.)  Added an SBBreakpointList class
so there's a way to specify which breakpoints to serialize and to report the
deserialized breakpoints.

<rdar://problem/12611863> 

llvm-svn: 281520
This commit is contained in:
Jim Ingham 2016-09-14 19:07:35 +00:00
parent db30877477
commit 01f1666471
11 changed files with 407 additions and 115 deletions

View File

@ -119,6 +119,7 @@ public:
GetNumBreakpointLocationsFromEvent(const lldb::SBEvent &event_sp);
private:
friend class SBBreakpointList;
friend class SBBreakpointLocation;
friend class SBTarget;
@ -139,6 +140,35 @@ private:
lldb::BreakpointSP m_opaque_sp;
};
class SBBreakpointListImpl;
class LLDB_API SBBreakpointList {
public:
SBBreakpointList(SBTarget &target);
~SBBreakpointList();
size_t GetSize() const;
SBBreakpoint GetBreakpointAtIndex(size_t idx);
void Append(const SBBreakpoint &sb_file);
bool AppendIfUnique(const SBBreakpoint &sb_file);
void AppendByID(lldb::break_id_t id);
void Clear();
protected:
friend class SBTarget;
void CopyToBreakpointIDList(lldb_private::BreakpointIDList &bp_id_list);
private:
std::shared_ptr<SBBreakpointListImpl> m_opaque_sp;
};
} // namespace lldb
#endif // LLDB_SBBreakpoint_h_

View File

@ -16,6 +16,7 @@
// Project includes
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBAttachInfo.h"
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBBroadcaster.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBFileSpec.h"
@ -641,6 +642,18 @@ public:
lldb::SBBreakpoint BreakpointCreateBySBAddress(SBAddress &address);
// Reads in breakpoints from source_file, returning the newly created
// breakpoints in new_bps.
lldb::SBError BreakpointsCreateFromFile(SBFileSpec &source_file,
SBBreakpointList &new_bps);
// Writes all breakpoints to dest_file.
lldb::SBError BreakpointsWriteToFile(SBFileSpec &dest_file);
// Writes the breakpoints in bkpt_list to dest_file
lldb::SBError BreakpointsWriteToFile(SBFileSpec &dest_file,
SBBreakpointList &bkpt_list);
uint32_t GetNumBreakpoints() const;
lldb::SBBreakpoint GetBreakpointAtIndex(uint32_t idx) const;
@ -741,6 +754,7 @@ public:
protected:
friend class SBAddress;
friend class SBBlock;
friend class SBBreakpointListImpl;
friend class SBDebugger;
friend class SBExecutionContext;
friend class SBFunction;

View File

@ -552,7 +552,7 @@ public:
static ObjectSP ParseJSON(std::string json_text);
static ObjectSP ParseJSONFromFile(FileSpec &file, Error &error);
static ObjectSP ParseJSONFromFile(const FileSpec &file, Error &error);
};
} // namespace lldb_private

View File

@ -678,6 +678,12 @@ public:
bool IgnoreWatchpointByID(lldb::watch_id_t watch_id, uint32_t ignore_count);
Error SerializeBreakpointsToFile(const FileSpec &file,
const BreakpointIDList &bp_ids);
Error CreateBreakpointsFromFile(const FileSpec &file,
BreakpointIDList &new_bps);
//------------------------------------------------------------------
/// Get \a load_addr as a callable code load address for this target
///
@ -1191,10 +1197,10 @@ protected:
Debugger &m_debugger;
lldb::PlatformSP m_platform_sp; ///< The platform for this target.
std::recursive_mutex m_mutex; ///< An API mutex that is used by the lldb::SB*
///classes make the SB interface thread safe
/// classes make the SB interface thread safe
ArchSpec m_arch;
ModuleList m_images; ///< The list of images for this process (shared
///libraries and anything dynamically loaded).
/// libraries and anything dynamically loaded).
SectionLoadHistory m_section_load_history;
BreakpointList m_breakpoint_list;
BreakpointList m_internal_breakpoint_list;

View File

@ -263,4 +263,29 @@ public:
};
class SBBreakpointListImpl;
class LLDB_API SBBreakpointList
{
public:
SBBreakpointList(SBTarget &target);
~SBBreakpointList();
size_t GetSize() const;
SBBreakpoint
GetBreakpointAtIndex(size_t idx);
void Append(const SBBreakpoint &sb_bkpt);
bool AppendIfUnique(const SBBreakpoint &sb_bkpt);
void AppendByID (lldb::break_id_t id);
void Clear();
private:
std::shared_ptr<SBBreakpointListImpl> m_opaque_sp;
};
} // namespace lldb

View File

@ -713,6 +713,16 @@ public:
bool
DeleteAllBreakpoints ();
lldb::SBError
BreakpointsCreateFromFile(SBFileSpec &source_file,
SBBreakpointList &bkpt_list);
lldb::SBError
BreakpointsWriteToFile(SBFileSpec &dest_file);
lldb::SBError
BreakpointsWriteToFile(SBFileSpec &dest_file, SBBreakpointList &bkpt_list);
uint32_t
GetNumWatchpoints () const;

View File

@ -21,6 +21,7 @@
#include "lldb/API/SBThread.h"
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointIDList.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Address.h"
@ -678,3 +679,128 @@ SBBreakpoint::GetNumBreakpointLocationsFromEvent(const lldb::SBEvent &event) {
event.GetSP()));
return num_locations;
}
// This is simple collection of breakpoint id's and their target.
class lldb::SBBreakpointListImpl {
public:
SBBreakpointListImpl(SBTarget &target) : m_target_wp() {
if (target.IsValid())
m_target_wp = target.GetSP();
}
~SBBreakpointListImpl() = default;
size_t GetSize() { return m_break_ids.size(); }
BreakpointSP GetBreakpointAtIndex(size_t idx) {
if (idx >= m_break_ids.size())
return BreakpointSP();
TargetSP target_sp = m_target_wp.lock();
if (!target_sp)
return BreakpointSP();
lldb::break_id_t bp_id = m_break_ids[idx];
return target_sp->GetBreakpointList().FindBreakpointByID(bp_id);
}
bool Append(Breakpoint &bkpt) {
TargetSP target_sp = m_target_wp.lock();
if (!target_sp)
return false;
if (bkpt.GetTargetSP() != target_sp)
return false;
m_break_ids.push_back(bkpt.GetID());
return true;
}
bool AppendIfUnique(Breakpoint &bkpt) {
TargetSP target_sp = m_target_wp.lock();
if (!target_sp)
return false;
if (bkpt.GetTargetSP() != target_sp)
return false;
lldb::break_id_t bp_id = bkpt.GetID();
if (find(m_break_ids.begin(), m_break_ids.end(), bp_id) ==
m_break_ids.end())
return false;
m_break_ids.push_back(bkpt.GetID());
return true;
}
bool AppendByID(lldb::break_id_t id) {
TargetSP target_sp = m_target_wp.lock();
if (!target_sp)
return false;
if (id == LLDB_INVALID_BREAK_ID)
return false;
m_break_ids.push_back(id);
return true;
}
void Clear() { m_break_ids.clear(); }
void CopyToBreakpointIDList(lldb_private::BreakpointIDList &bp_list) {
for (lldb::break_id_t id : m_break_ids) {
bp_list.AddBreakpointID(BreakpointID(id));
}
}
TargetSP GetTarget() { return m_target_wp.lock(); }
private:
std::vector<lldb::break_id_t> m_break_ids;
TargetWP m_target_wp;
};
SBBreakpointList::SBBreakpointList(SBTarget &target)
: m_opaque_sp(new lldb::SBBreakpointListImpl(target)) {}
SBBreakpointList::~SBBreakpointList() {}
size_t SBBreakpointList::GetSize() const {
if (!m_opaque_sp)
return 0;
else
return m_opaque_sp->GetSize();
}
SBBreakpoint SBBreakpointList::GetBreakpointAtIndex(size_t idx) {
if (!m_opaque_sp)
return SBBreakpoint();
BreakpointSP bkpt_sp = m_opaque_sp->GetBreakpointAtIndex(idx);
return SBBreakpoint(bkpt_sp);
}
void SBBreakpointList::Append(const SBBreakpoint &sb_bkpt) {
if (!sb_bkpt.IsValid())
return;
if (!m_opaque_sp)
return;
m_opaque_sp->Append(*sb_bkpt.get());
}
void SBBreakpointList::AppendByID(lldb::break_id_t id) {
if (!m_opaque_sp)
return;
m_opaque_sp->AppendByID(id);
}
bool SBBreakpointList::AppendIfUnique(const SBBreakpoint &sb_bkpt) {
if (!sb_bkpt.IsValid())
return false;
if (!m_opaque_sp)
return false;
return m_opaque_sp->AppendIfUnique(*sb_bkpt.get());
}
void SBBreakpointList::Clear() {
if (m_opaque_sp)
m_opaque_sp->Clear();
}
void SBBreakpointList::CopyToBreakpointIDList(
lldb_private::BreakpointIDList &bp_id_list) {
if (m_opaque_sp)
m_opaque_sp->CopyToBreakpointIDList(bp_id_list);
}

View File

@ -1097,6 +1097,58 @@ bool SBTarget::DeleteAllBreakpoints() {
return false;
}
lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
SBBreakpointList &new_bps) {
SBError sberr;
TargetSP target_sp(GetSP());
if (!target_sp) {
sberr.SetErrorString(
"BreakpointCreateFromFile called with invalid target.");
return sberr;
}
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
BreakpointIDList bp_ids;
sberr.ref() = target_sp->CreateBreakpointsFromFile(source_file.ref(), bp_ids);
if (sberr.Fail())
return sberr;
size_t num_bkpts = bp_ids.GetSize();
for (size_t i = 0; i < num_bkpts; i++) {
BreakpointID bp_id = bp_ids.GetBreakpointIDAtIndex(i);
new_bps.AppendByID(bp_id.GetBreakpointID());
}
return sberr;
}
lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file) {
SBError sberr;
TargetSP target_sp(GetSP());
if (!target_sp) {
sberr.SetErrorString("BreakpointWriteToFile called with invalid target.");
return sberr;
}
SBBreakpointList bkpt_list(*this);
return BreakpointsWriteToFile(dest_file, bkpt_list);
}
lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file,
SBBreakpointList &bkpt_list) {
SBError sberr;
TargetSP target_sp(GetSP());
if (!target_sp) {
sberr.SetErrorString("BreakpointWriteToFile called with invalid target.");
return sberr;
}
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
BreakpointIDList bp_id_list;
bkpt_list.CopyToBreakpointIDList(bp_id_list);
sberr.ref() =
target_sp->SerializeBreakpointsToFile(dest_file.ref(), bp_id_list);
return sberr;
}
uint32_t SBTarget::GetNumWatchpoints() const {
TargetSP target_sp(GetSP());
if (target_sp) {

View File

@ -2155,57 +2155,16 @@ protected:
return false;
}
std::unique_lock<std::recursive_mutex> lock;
target->GetBreakpointList().GetListMutex(lock);
FileSpec input_spec(m_options.m_filename, true);
Error error;
StructuredData::ObjectSP input_data_sp =
StructuredData::ParseJSONFromFile(input_spec, error);
BreakpointIDList new_bps;
Error error = target->CreateBreakpointsFromFile(input_spec, new_bps);
if (!error.Success()) {
result.AppendErrorWithFormat("Error reading data from input file: %s.",
error.AsCString());
result.SetStatus(eReturnStatusFailed);
return false;
} else if (!input_data_sp || !input_data_sp->IsValid()) {
result.AppendErrorWithFormat("Invalid JSON from input file: %s.",
input_spec.GetPath().c_str());
result.AppendError(error.AsCString());
result.SetStatus(eReturnStatusFailed);
return false;
}
StructuredData::Array *bkpt_array = input_data_sp->GetAsArray();
if (!bkpt_array) {
result.AppendErrorWithFormat(
"Invalid breakpoint data from input file: %s.",
input_spec.GetPath().c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
size_t num_bkpts = bkpt_array->GetSize();
for (size_t i = 0; i < num_bkpts; i++) {
StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i);
// Peel off the breakpoint key, and feed the rest to the Breakpoint:
StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary();
if (!bkpt_dict) {
result.AppendErrorWithFormat(
"Invalid breakpoint data for element %zu from input file: %s.", i,
input_spec.GetPath().c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
StructuredData::ObjectSP bkpt_data_sp =
bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey());
BreakpointSP bkpt_sp =
Breakpoint::CreateFromStructuredData(*target, bkpt_data_sp, error);
if (!error.Success()) {
result.AppendErrorWithFormat(
"Error restoring breakpoint %zu from %s: %s.", i,
input_spec.GetPath().c_str(), error.AsCString());
result.SetStatus(eReturnStatusFailed);
}
}
// FIXME: Report the newly created breakpoints.
return result.Succeeded();
}
@ -2295,78 +2254,26 @@ protected:
return false;
}
// Before we do anything else make sure we can actually write to this file:
StreamFile out_file(m_options.m_filename.c_str(),
File::OpenOptions::eOpenOptionTruncate |
File::OpenOptions::eOpenOptionWrite |
File::OpenOptions::eOpenOptionCanCreate |
File::OpenOptions::eOpenOptionCloseOnExec,
lldb::eFilePermissionsFileDefault);
if (!out_file.GetFile().IsValid()) {
result.AppendErrorWithFormat("Unable to open output file: %s.",
m_options.m_filename.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
std::unique_lock<std::recursive_mutex> lock;
target->GetBreakpointList().GetListMutex(lock);
StructuredData::ArraySP break_store_sp(new StructuredData::Array());
if (command.GetArgumentCount() == 0) {
const BreakpointList &breakpoints = target->GetBreakpointList();
size_t num_breakpoints = breakpoints.GetSize();
for (size_t i = 0; i < num_breakpoints; i++) {
Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData();
// If a breakpoint can't serialize it, just ignore it for now:
if (bkpt_save_sp)
break_store_sp->AddItem(bkpt_save_sp);
}
} else {
BreakpointIDList valid_bp_ids;
BreakpointIDList valid_bp_ids;
if (command.GetArgumentCount() > 0) {
CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
command, target, result, &valid_bp_ids);
if (result.Succeeded()) {
std::unordered_set<lldb::break_id_t> processed_bkpts;
const size_t count = valid_bp_ids.GetSize();
for (size_t i = 0; i < count; ++i) {
BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID();
if (bp_id != LLDB_INVALID_BREAK_ID) {
// Only do each breakpoint once:
std::pair<std::unordered_set<lldb::break_id_t>::iterator, bool>
insert_result = processed_bkpts.insert(bp_id);
if (!insert_result.second)
continue;
Breakpoint *bp = target->GetBreakpointByID(bp_id).get();
StructuredData::ObjectSP bkpt_save_sp =
bp->SerializeToStructuredData();
// If the user explicitly asked to serialize a breakpoint, and we
// can't, then
// raise an error:
if (!bkpt_save_sp) {
result.AppendErrorWithFormat("Unable to serialize breakpoint %d",
bp_id);
result.SetStatus(eReturnStatusFailed);
return false;
}
break_store_sp->AddItem(bkpt_save_sp);
}
}
if (!result.Succeeded()) {
result.SetStatus(eReturnStatusFailed);
return false;
}
}
break_store_sp->Dump(out_file, false);
out_file.PutChar('\n');
Error error = target->SerializeBreakpointsToFile(
FileSpec(m_options.m_filename.c_str(), true), valid_bp_ids);
if (!error.Success()) {
result.AppendErrorWithFormat("error serializing breakpoints: %s.",
error.AsCString());
result.SetStatus(eReturnStatusFailed);
}
return result.Succeeded();
}

View File

@ -31,8 +31,8 @@ static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser);
static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser);
static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser);
StructuredData::ObjectSP StructuredData::ParseJSONFromFile(FileSpec &input_spec,
Error &error) {
StructuredData::ObjectSP
StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Error &error) {
StructuredData::ObjectSP return_sp;
if (!input_spec.Exists()) {
error.SetErrorStringWithFormat("input file %s does not exist.",

View File

@ -15,6 +15,7 @@
#include "Plugins/ExpressionParser/Clang/ClangASTSource.h"
#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
#include "lldb/Breakpoint/BreakpointIDList.h"
#include "lldb/Breakpoint/BreakpointResolver.h"
#include "lldb/Breakpoint/BreakpointResolverAddress.h"
#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
@ -794,6 +795,127 @@ bool Target::EnableBreakpointByID(break_id_t break_id) {
return false;
}
Error Target::SerializeBreakpointsToFile(const FileSpec &file,
const BreakpointIDList &bp_ids) {
Error error;
if (!file) {
error.SetErrorString("Invalid FileSpec.");
return error;
}
std::string path(file.GetPath());
StreamFile out_file(path.c_str(),
File::OpenOptions::eOpenOptionTruncate |
File::OpenOptions::eOpenOptionWrite |
File::OpenOptions::eOpenOptionCanCreate |
File::OpenOptions::eOpenOptionCloseOnExec,
lldb::eFilePermissionsFileDefault);
if (!out_file.GetFile().IsValid()) {
error.SetErrorStringWithFormat("Unable to open output file: %s.",
path.c_str());
return error;
}
std::unique_lock<std::recursive_mutex> lock;
GetBreakpointList().GetListMutex(lock);
StructuredData::ArraySP break_store_sp(new StructuredData::Array());
if (bp_ids.GetSize() == 0) {
const BreakpointList &breakpoints = GetBreakpointList();
size_t num_breakpoints = breakpoints.GetSize();
for (size_t i = 0; i < num_breakpoints; i++) {
Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData();
// If a breakpoint can't serialize it, just ignore it for now:
if (bkpt_save_sp)
break_store_sp->AddItem(bkpt_save_sp);
}
} else {
std::unordered_set<lldb::break_id_t> processed_bkpts;
const size_t count = bp_ids.GetSize();
for (size_t i = 0; i < count; ++i) {
BreakpointID cur_bp_id = bp_ids.GetBreakpointIDAtIndex(i);
lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID();
if (bp_id != LLDB_INVALID_BREAK_ID) {
// Only do each breakpoint once:
std::pair<std::unordered_set<lldb::break_id_t>::iterator, bool>
insert_result = processed_bkpts.insert(bp_id);
if (!insert_result.second)
continue;
Breakpoint *bp = GetBreakpointByID(bp_id).get();
StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData();
// If the user explicitly asked to serialize a breakpoint, and we
// can't, then
// raise an error:
if (!bkpt_save_sp) {
error.SetErrorStringWithFormat("Unable to serialize breakpoint %d",
bp_id);
return error;
}
break_store_sp->AddItem(bkpt_save_sp);
}
}
}
break_store_sp->Dump(out_file, false);
out_file.PutChar('\n');
return error;
}
Error Target::CreateBreakpointsFromFile(const FileSpec &file,
BreakpointIDList &new_bps) {
std::unique_lock<std::recursive_mutex> lock;
GetBreakpointList().GetListMutex(lock);
Error error;
StructuredData::ObjectSP input_data_sp =
StructuredData::ParseJSONFromFile(file, error);
if (!error.Success()) {
return error;
} else if (!input_data_sp || !input_data_sp->IsValid()) {
error.SetErrorStringWithFormat("Invalid JSON from input file: %s.",
file.GetPath().c_str());
return error;
}
StructuredData::Array *bkpt_array = input_data_sp->GetAsArray();
if (!bkpt_array) {
error.SetErrorStringWithFormat(
"Invalid breakpoint data from input file: %s.", file.GetPath().c_str());
return error;
}
size_t num_bkpts = bkpt_array->GetSize();
for (size_t i = 0; i < num_bkpts; i++) {
StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i);
// Peel off the breakpoint key, and feed the rest to the Breakpoint:
StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary();
if (!bkpt_dict) {
error.SetErrorStringWithFormat(
"Invalid breakpoint data for element %zu from input file: %s.", i,
file.GetPath().c_str());
return error;
}
StructuredData::ObjectSP bkpt_data_sp =
bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey());
BreakpointSP bkpt_sp =
Breakpoint::CreateFromStructuredData(*this, bkpt_data_sp, error);
if (!error.Success()) {
error.SetErrorStringWithFormat(
"Error restoring breakpoint %zu from %s: %s.", i,
file.GetPath().c_str(), error.AsCString());
return error;
}
new_bps.AddBreakpointID(BreakpointID(bkpt_sp->GetID()));
}
return error;
}
// The flag 'end_to_end', default to true, signifies that the operation is
// performed end to end, for both the debugger and the debuggee.