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
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index bd8ae56..0b612c6 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -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 @@
   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.