Add StructuredData plugin type; showcase with new DarwinLog feature

Take 2, with missing cmake line fixed.  Build tested on
Ubuntu 14.04 with clang-3.6.

See docs/structured_data/StructuredDataPlugins.md for details.

differential review: https://reviews.llvm.org/D22976

reviewers: clayborg, jingham
llvm-svn: 279202
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
index 786d801..fde3803 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -11,6 +11,7 @@
 
 #include "llvm/ADT/StringExtras.h"
 
+#include "lldb/Target/Process.h"
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/LLDBAssert.h"
 
@@ -100,6 +101,41 @@
             case 'A':
                 delegate.HandleAsyncMisc(llvm::StringRef(response.GetStringRef()).substr(1));
                 break;
+
+            case 'J':
+                // Asynchronous JSON packet, destined for a
+                // StructuredDataPlugin.
+            {
+                // Parse the content into a StructuredData instance.
+                auto payload_index = strlen("JSON-async:");
+                StructuredData::ObjectSP json_sp =
+                    StructuredData::ParseJSON(response.GetStringRef()
+                                              .substr(payload_index));
+                if (log)
+                {
+                    if (json_sp)
+                        log->Printf(
+                                    "GDBRemoteCommmunicationClientBase::%s() "
+                                    "received Async StructuredData packet: %s",
+                                    __FUNCTION__,
+                                    response.GetStringRef().
+                                    substr(payload_index).c_str());
+                    else
+                        log->Printf("GDBRemoteCommmunicationClientBase::%s"
+                                    "() received StructuredData packet:"
+                                    " parse failure", __FUNCTION__);
+                }
+
+                // Pass the data to the process to route to the
+                // appropriate plugin.  The plugin controls what happens
+                // to it from there.
+                bool routed = delegate.HandleAsyncStructuredData(json_sp);
+                if (log)
+                    log->Printf("GDBRemoteCommmunicationClientBase::%s()"
+                                " packet %s", __FUNCTION__,
+                                routed ? "handled" : "not handled");
+                break;
+            }
             case 'T':
             case 'S':
                 // Do this with the continue lock held.
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
index 9cfba4b..684ef3e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -31,6 +31,16 @@
         HandleAsyncMisc(llvm::StringRef data) = 0;
         virtual void
         HandleStopReply() = 0;
+
+        //
+        /// Processes async structured data.
+        ///
+        /// @return
+        ///    true if the data was handled; otherwise, false.
+        //
+        virtual bool
+        HandleAsyncStructuredData(const StructuredData::ObjectSP
+                                  &object_sp) = 0;
     };
 
     GDBRemoteClientBase(const char *comm_name, const char *listener_name);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 00357dd..d893b86 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -30,6 +30,8 @@
 #include "lldb/Interpreter/Args.h"
 #include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
 #include "lldb/Target/Target.h"
 
 // Project includes
@@ -114,7 +116,10 @@
       m_gdb_server_name(),
       m_gdb_server_version(UINT32_MAX),
       m_default_packet_timeout(0),
-      m_max_packet_size(0)
+      m_max_packet_size(0),
+      m_qSupported_response(),
+      m_supported_async_json_packets_is_valid(false),
+      m_supported_async_json_packets_sp()
 {
 }
 
@@ -378,6 +383,9 @@
         m_gdb_server_version = UINT32_MAX;
         m_default_packet_timeout = 0;
         m_max_packet_size = 0;
+        m_qSupported_response.clear();
+        m_supported_async_json_packets_is_valid = false;
+        m_supported_async_json_packets_sp.reset();
     }
 
     // These flags should be reset when we first connect to a GDB server
@@ -413,6 +421,12 @@
                                      /*send_async=*/false) == PacketResult::Success)
     {
         const char *response_cstr = response.GetStringRef().c_str();
+
+        // Hang on to the qSupported packet, so that platforms can do custom
+        // configuration of the transport before attaching/launching the
+        // process.
+        m_qSupported_response = response_cstr;
+
         if (::strstr (response_cstr, "qXfer:auxv:read+"))
             m_supports_qXfer_auxv_read = eLazyBoolYes;
         if (::strstr (response_cstr, "qXfer:libraries-svr4:read+"))
@@ -3906,6 +3920,126 @@
     }
 }
 
+StructuredData::Array*
+GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins()
+{
+    if (!m_supported_async_json_packets_is_valid)
+    {
+        // Query the server for the array of supported asynchronous JSON
+        // packets.
+        m_supported_async_json_packets_is_valid = true;
+
+        Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(
+            GDBR_LOG_PROCESS));
+
+        // Poll it now.
+        StringExtractorGDBRemote response;
+        const bool send_async = false;
+        if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response,
+                                         send_async) == PacketResult::Success)
+        {
+            m_supported_async_json_packets_sp = StructuredData::ParseJSON(
+                response.GetStringRef());
+            if (m_supported_async_json_packets_sp &&
+                !m_supported_async_json_packets_sp->GetAsArray())
+            {
+                // We were returned something other than a JSON array.  This
+                // is invalid.  Clear it out.
+                if (log)
+                    log->Printf("GDBRemoteCommunicationClient::%s(): "
+                                "QSupportedAsyncJSONPackets returned invalid "
+                                "result: %s", __FUNCTION__,
+                                response.GetStringRef().c_str());
+                m_supported_async_json_packets_sp.reset();
+            }
+        }
+        else
+        {
+            if (log)
+                log->Printf("GDBRemoteCommunicationClient::%s(): "
+                            "QSupportedAsyncJSONPackets unsupported",
+                            __FUNCTION__);
+        }
+
+        if (log && m_supported_async_json_packets_is_valid)
+        {
+            StreamString  stream;
+            m_supported_async_json_packets_sp->Dump(stream);
+            log->Printf("GDBRemoteCommunicationClient::%s(): supported async "
+                        "JSON packets: %s", __FUNCTION__,
+                        stream.GetString().c_str());
+        }
+    }
+
+    return m_supported_async_json_packets_sp
+        ? m_supported_async_json_packets_sp->GetAsArray()
+        : nullptr;
+}
+
+Error
+GDBRemoteCommunicationClient::ConfigureRemoteStructuredData(
+    const ConstString &type_name,
+    const StructuredData::ObjectSP &config_sp)
+{
+    Error error;
+
+    if (type_name.GetLength() == 0)
+    {
+        error.SetErrorString("invalid type_name argument");
+        return error;
+    }
+
+    // Build command: Configure{type_name}: serialized config
+    // data.
+    StreamGDBRemote stream;
+    stream.PutCString("QConfigure");
+    stream.PutCString(type_name.AsCString());
+    stream.PutChar(':');
+    if (config_sp)
+    {
+        // Gather the plain-text version of the configuration data.
+        StreamString unescaped_stream;
+        config_sp->Dump(unescaped_stream);
+        unescaped_stream.Flush();
+
+        // Add it to the stream in escaped fashion.
+        stream.PutEscapedBytes(unescaped_stream.GetData(),
+                               unescaped_stream.GetSize());
+    }
+    stream.Flush();
+
+    // Send the packet.
+    const bool send_async = false;
+    StringExtractorGDBRemote response;
+    auto result = SendPacketAndWaitForResponse(stream.GetString().c_str(),
+                                               response, send_async);
+    if (result == PacketResult::Success)
+    {
+        // We failed if the config result comes back other than OK.
+        if (strcmp(response.GetStringRef().c_str(), "OK") == 0)
+        {
+            // Okay!
+            error.Clear();
+        }
+        else
+        {
+            error.SetErrorStringWithFormat("configuring StructuredData feature "
+                                           "%s failed with error %s",
+                                           type_name.AsCString(),
+                                           response.GetStringRef().c_str());
+        }
+    }
+    else
+    {
+        // Can we get more data here on the failure?
+        error.SetErrorStringWithFormat("configuring StructuredData feature %s "
+                                       "failed when sending packet: "
+                                       "PacketResult=%d", type_name.AsCString(),
+                                       result);
+    }
+    return error;
+}
+
 void
 GDBRemoteCommunicationClient::OnRunPacketSent(bool first)
 {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index b041ecb..54e7079 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -547,6 +547,53 @@
     void
     ServeSymbolLookups(lldb_private::Process *process);
 
+    //------------------------------------------------------------------
+    /// Return the feature set supported by the gdb-remote server.
+    ///
+    /// This method returns the remote side's response to the qSupported
+    /// packet.  The response is the complete string payload returned
+    /// to the client.
+    ///
+    /// @return
+    ///     The string returned by the server to the qSupported query.
+    //------------------------------------------------------------------
+    const std::string&
+    GetServerSupportedFeatures() const
+    {
+        return m_qSupported_response;
+    }
+
+    //------------------------------------------------------------------
+    /// Return the array of async JSON packet types supported by the remote.
+    ///
+    /// This method returns the remote side's array of supported JSON
+    /// packet types as a list of type names.  Each of the results are
+    /// expected to have an Enable{type_name} command to enable and configure
+    /// the related feature.  Each type_name for an enabled feature will
+    /// possibly send async-style packets that contain a payload of a
+    /// binhex-encoded JSON dictionary.  The dictionary will have a
+    /// string field named 'type', that contains the type_name of the
+    /// supported packet type.
+    ///
+    /// There is a Plugin category called structured-data plugins.
+    /// A plugin indicates whether it knows how to handle a type_name.
+    /// If so, it can be used to process the async JSON packet.
+    ///
+    /// @return
+    ///     The string returned by the server to the qSupported query.
+    //------------------------------------------------------------------
+    lldb_private::StructuredData::Array*
+    GetSupportedStructuredDataPlugins();
+
+    //------------------------------------------------------------------
+    /// Configure a StructuredData feature on the remote end.
+    ///
+    /// @see \b Process::ConfigureStructuredData(...) for details.
+    //------------------------------------------------------------------
+    Error
+    ConfigureRemoteStructuredData(const ConstString &type_name,
+                                  const StructuredData::ObjectSP &config_sp);
+
 protected:
     LazyBool m_supports_not_sending_acks;
     LazyBool m_supports_thread_suffix;
@@ -617,6 +664,10 @@
     uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if qGDBServerVersion is not supported
     uint32_t m_default_packet_timeout;
     uint64_t m_max_packet_size;  // as returned by qSupported
+    std::string m_qSupported_response; // the complete response to qSupported
+
+    bool m_supported_async_json_packets_is_valid;
+    lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
 
     bool
     GetCurrentProcessInfo (bool allow_lazy_pid = true);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 1fa78fe..be5c3b5 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1175,7 +1175,7 @@
 {
     Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
     if (log)
-        log->Printf ("ProcessGDBRemote::DidLaunch()");
+        log->Printf ("ProcessGDBRemote::%s()", __FUNCTION__);
     if (GetID() != LLDB_INVALID_PROCESS_ID)
     {
         BuildDynamicRegisterInfo (false);
@@ -1271,6 +1271,13 @@
                 GetTarget().SetArchitecture (process_arch);
             }
         }
+
+        // Find out which StructuredDataPlugins are supported by the
+        // debug monitor.  These plugins transmit data over async $J packets.
+        auto supported_packets_array =
+            m_gdb_comm.GetSupportedStructuredDataPlugins();
+        if (supported_packets_array)
+            MapSupportedStructuredDataPlugins(*supported_packets_array);
     }
 }
 
@@ -4342,6 +4349,13 @@
     return object_sp;
 }
 
+Error
+ProcessGDBRemote::ConfigureStructuredData(const ConstString &type_name,
+                                          const StructuredData::ObjectSP
+                                          &config_sp)
+{
+    return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp);
+}
 
 // Establish the largest memory read/write payloads we should use.
 // If the remote stub has a max packet size, stay under that size.
@@ -5235,6 +5249,13 @@
     BuildDynamicRegisterInfo(true);
 }
 
+bool
+ProcessGDBRemote::HandleAsyncStructuredData(const StructuredData::ObjectSP
+                                            &object_sp)
+{
+    return RouteAsyncStructuredData(object_sp);
+}
+
 class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed
 {
 public:
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 0f41a21..3a0dd2f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -261,6 +261,10 @@
     StructuredData::ObjectSP
     GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) override;
 
+    Error
+    ConfigureStructuredData(const ConstString &type_name,
+                            const StructuredData::ObjectSP &config_sp) override;
+
     StructuredData::ObjectSP
     GetLoadedDynamicLibrariesInfos () override;
 
@@ -506,6 +510,9 @@
     HandleAsyncMisc(llvm::StringRef data) override;
     void
     HandleStopReply() override;
+    bool
+    HandleAsyncStructuredData(const StructuredData::ObjectSP
+                              &object_sp) override;
 
     DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
 };