Add the ability to deserialize only breakpoints matching a given name.

Also tests for this and the ThreadSpec serialization.

llvm-svn: 282207
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index 8616d22..54ed098 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -1128,6 +1128,13 @@
 
 lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
                                                   SBBreakpointList &new_bps) {
+  SBStringList empty_name_list;
+  return BreakpointsCreateFromFile(source_file, empty_name_list, new_bps);
+}
+
+lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
+                                                  SBStringList &matching_names,
+                                                  SBBreakpointList &new_bps) {
   SBError sberr;
   TargetSP target_sp(GetSP());
   if (!target_sp) {
@@ -1138,7 +1145,14 @@
   std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
 
   BreakpointIDList bp_ids;
-  sberr.ref() = target_sp->CreateBreakpointsFromFile(source_file.ref(), bp_ids);
+
+  std::vector<std::string> name_vector;
+  size_t num_names = matching_names.GetSize();
+  for (size_t i = 0; i < num_names; i++)
+    name_vector.push_back(matching_names.GetStringAtIndex(i));
+
+  sberr.ref() = target_sp->CreateBreakpointsFromFile(source_file.ref(),
+                                                     name_vector, bp_ids);
   if (sberr.Fail())
     return sberr;
 
diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp
index a9666f4..9f4376c 100644
--- a/lldb/source/Breakpoint/Breakpoint.cpp
+++ b/lldb/source/Breakpoint/Breakpoint.cpp
@@ -216,6 +216,41 @@
   return result_sp;
 }
 
+bool Breakpoint::SerializedBreakpointMatchesNames(
+    StructuredData::ObjectSP &bkpt_object_sp, std::vector<std::string> &names) {
+  if (!bkpt_object_sp)
+    return false;
+
+  StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary();
+  if (!bkpt_dict)
+    return false;
+
+  if (names.empty())
+    return true;
+
+  StructuredData::Array *names_array;
+
+  bool success =
+      bkpt_dict->GetValueForKeyAsArray(GetKey(OptionNames::Names), names_array);
+  // If there are no names, it can't match these names;
+  if (!success)
+    return false;
+
+  size_t num_names = names_array->GetSize();
+  std::vector<std::string>::iterator begin = names.begin();
+  std::vector<std::string>::iterator end = names.end();
+
+  for (size_t i = 0; i < num_names; i++) {
+    std::string name;
+    if (names_array->GetItemAtIndexAsString(i, name)) {
+      if (std::find(begin, end, name) != end) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 const lldb::TargetSP Breakpoint::GetTargetSP() {
   return m_target.shared_from_this();
 }
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 13235d7..1e90b57 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -2077,10 +2077,11 @@
 //-------------------------------------------------------------------------
 // CommandObjectBreakpointRead
 //-------------------------------------------------------------------------
-#pragma mark Modify::CommandOptions
+#pragma mark Read::CommandOptions
 static OptionDefinition g_breakpoint_read_options[] = {
     // clang-format off
-  { LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eDiskFileCompletion, eArgTypeFilename,    "The file from which to read the breakpoints." },
+  { LLDB_OPT_SET_ALL, true, "file",                   'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eDiskFileCompletion, eArgTypeFilename,       "The file from which to read the breakpoints." },
+  {LLDB_OPT_SET_ALL, false, "breakpoint-name",        'N', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                       eArgTypeBreakpointName, "Only read in breakpoints with this name."},
     // clang-format on
 };
 
@@ -2121,6 +2122,16 @@
       case 'f':
         m_filename.assign(option_arg);
         break;
+      case 'N': {
+        Error name_error;
+        if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(option_arg),
+                                                  name_error)) {
+          error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
+                                         name_error.AsCString());
+        }
+        m_names.push_back(option_arg);
+        break;
+      }
       default:
         error.SetErrorStringWithFormat("unrecognized option '%c'",
                                        short_option);
@@ -2132,6 +2143,7 @@
 
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_filename.clear();
+      m_names.clear();
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2141,6 +2153,7 @@
     // Instance variables to hold the values for command options.
 
     std::string m_filename;
+    std::vector<std::string> m_names;
   };
 
 protected:
@@ -2152,16 +2165,38 @@
       return false;
     }
 
+    std::unique_lock<std::recursive_mutex> lock;
+    target->GetBreakpointList().GetListMutex(lock);
+
     FileSpec input_spec(m_options.m_filename, true);
     BreakpointIDList new_bps;
-    Error error = target->CreateBreakpointsFromFile(input_spec, new_bps);
+    Error error = target->CreateBreakpointsFromFile(input_spec,
+                                                    m_options.m_names, new_bps);
 
     if (!error.Success()) {
       result.AppendError(error.AsCString());
       result.SetStatus(eReturnStatusFailed);
       return false;
     }
-    // FIXME: Report the newly created breakpoints.
+
+    Stream &output_stream = result.GetOutputStream();
+
+    size_t num_breakpoints = new_bps.GetSize();
+    if (num_breakpoints == 0) {
+      result.AppendMessage("No breakpoints added.");
+    } else {
+      // No breakpoint selected; show info about all currently set breakpoints.
+      result.AppendMessage("New breakpoints:");
+      for (size_t i = 0; i < num_breakpoints; ++i) {
+        BreakpointID bp_id = new_bps.GetBreakpointIDAtIndex(i);
+        Breakpoint *bp = target->GetBreakpointList()
+                             .FindBreakpointByID(bp_id.GetBreakpointID())
+                             .get();
+        if (bp)
+          bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                             false);
+      }
+    }
     return result.Succeeded();
   }
 
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 0b612c6..d73cc84 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -869,6 +869,13 @@
 
 Error Target::CreateBreakpointsFromFile(const FileSpec &file,
                                         BreakpointIDList &new_bps) {
+  std::vector<std::string> no_names;
+  return CreateBreakpointsFromFile(file, no_names, new_bps);
+}
+
+Error Target::CreateBreakpointsFromFile(const FileSpec &file,
+                                        std::vector<std::string> &names,
+                                        BreakpointIDList &new_bps) {
   std::unique_lock<std::recursive_mutex> lock;
   GetBreakpointList().GetListMutex(lock);
 
@@ -891,6 +898,8 @@
   }
 
   size_t num_bkpts = bkpt_array->GetSize();
+  size_t num_names = names.size();
+
   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:
@@ -903,6 +912,10 @@
     }
     StructuredData::ObjectSP bkpt_data_sp =
         bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey());
+    if (num_names &&
+        !Breakpoint::SerializedBreakpointMatchesNames(bkpt_data_sp, names))
+      continue;
+
     BreakpointSP bkpt_sp =
         Breakpoint::CreateFromStructuredData(*this, bkpt_data_sp, error);
     if (!error.Success()) {