summaryrefslogtreecommitdiffstats
path: root/lldb/tools/debugserver/source
diff options
context:
space:
mode:
authorTodd Fiala <todd.fiala@gmail.com>2016-08-19 04:21:48 +0000
committerTodd Fiala <todd.fiala@gmail.com>2016-08-19 04:21:48 +0000
commit759300192abe8e38e8a40ab95934ba602a78c252 (patch)
treed284c51cf10c23d3023ab8e1308e26ad1ee218a7 /lldb/tools/debugserver/source
parente2ca3b65fcba7c4c5dbd10c1e8925177b7718806 (diff)
downloadbcm5719-llvm-759300192abe8e38e8a40ab95934ba602a78c252.tar.gz
bcm5719-llvm-759300192abe8e38e8a40ab95934ba602a78c252.zip
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
Diffstat (limited to 'lldb/tools/debugserver/source')
-rw-r--r--lldb/tools/debugserver/source/CMakeLists.txt6
-rw-r--r--lldb/tools/debugserver/source/DNB.cpp7
-rw-r--r--lldb/tools/debugserver/source/DNB.h3
-rw-r--r--lldb/tools/debugserver/source/DNBDefs.h1
-rw-r--r--lldb/tools/debugserver/source/JSON.cpp746
-rw-r--r--lldb/tools/debugserver/source/JSON.h382
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CMakeLists.txt3
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp18
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h36
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h200
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt15
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp835
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h139
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h27
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h25
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h23
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp14
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h44
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp61
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h48
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp57
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h36
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp118
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h58
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp19
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h53
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp95
-rw-r--r--lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h73
-rw-r--r--lldb/tools/debugserver/source/MacOSX/OsLogger.cpp71
-rw-r--r--lldb/tools/debugserver/source/MacOSX/OsLogger.h24
-rw-r--r--lldb/tools/debugserver/source/RNBContext.cpp2
-rw-r--r--lldb/tools/debugserver/source/RNBContext.h20
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp267
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.h6
-rw-r--r--lldb/tools/debugserver/source/debugserver.cpp24
35 files changed, 3527 insertions, 29 deletions
diff --git a/lldb/tools/debugserver/source/CMakeLists.txt b/lldb/tools/debugserver/source/CMakeLists.txt
index 94cef6c3120..b309d99a555 100644
--- a/lldb/tools/debugserver/source/CMakeLists.txt
+++ b/lldb/tools/debugserver/source/CMakeLists.txt
@@ -1,5 +1,6 @@
include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
include_directories(${LLDB_SOURCE_DIR}/source)
+include_directories(MacOSX/DarwinLog)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
include_directories(MacOSX)
@@ -42,6 +43,11 @@ add_library(lldbDebugserverCommon
DNBLog.cpp
DNBRegisterInfo.cpp
DNBThreadResumeActions.cpp
+ JSON.cpp
+ # JSON reader depends on the following LLDB-common files
+ ${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp
+ ${LLDB_SOURCE_DIR}/source/Utility/StringExtractor.cpp
+ # end JSON reader dependencies
libdebugserver.cpp
PseudoTerminal.cpp
PThreadEvent.cpp
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp
index bb360364919..fc36dcec078 100644
--- a/lldb/tools/debugserver/source/DNB.cpp
+++ b/lldb/tools/debugserver/source/DNB.cpp
@@ -41,6 +41,7 @@
#endif
#endif
+#include "MacOSX/DarwinLog/DarwinLogCollector.h"
#include "MacOSX/MachProcess.h"
#include "MacOSX/MachTask.h"
#include "MacOSX/Genealogy.h"
@@ -1868,6 +1869,12 @@ DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_
return 0;
}
+DarwinLogEventVector
+DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid)
+{
+ return DarwinLogCollector::GetEventsForProcess(pid);
+}
+
nub_size_t
DNBProcessGetStopCount (nub_process_t pid)
{
diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h
index fbaf5e34813..3e928e663d7 100644
--- a/lldb/tools/debugserver/source/DNB.h
+++ b/lldb/tools/debugserver/source/DNB.h
@@ -14,6 +14,7 @@
#ifndef __DNB_h__
#define __DNB_h__
+#include "MacOSX/DarwinLog/DarwinLogEvent.h"
#include "MacOSX/Genealogy.h"
#include "MacOSX/ThreadInfo.h"
#include "JSONGenerator.h"
@@ -81,6 +82,8 @@ nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr)
int DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT;
std::string DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT;
nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) DNB_EXPORT;
+DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid);
+
//----------------------------------------------------------------------
// Process status
diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h
index e3757e903e5..36efad22417 100644
--- a/lldb/tools/debugserver/source/DNBDefs.h
+++ b/lldb/tools/debugserver/source/DNBDefs.h
@@ -142,6 +142,7 @@ enum
#define LOG_WATCHPOINTS (1u << 11)
#define LOG_STEP (1u << 12)
#define LOG_TASK (1u << 13)
+#define LOG_DARWIN_LOG (1u << 14)
#define LOG_LO_USER (1u << 16)
#define LOG_HI_USER (1u << 31)
#define LOG_ALL 0xFFFFFFFFu
diff --git a/lldb/tools/debugserver/source/JSON.cpp b/lldb/tools/debugserver/source/JSON.cpp
new file mode 100644
index 00000000000..e7e0423e9e8
--- /dev/null
+++ b/lldb/tools/debugserver/source/JSON.cpp
@@ -0,0 +1,746 @@
+//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSON.h"
+
+// C includes
+#include <assert.h>
+#include <limits.h>
+
+// C++ includes
+#include <iomanip>
+#include <sstream>
+#include "lldb/Host/StringConvert.h"
+
+using namespace lldb_private;
+
+std::string
+JSONString::json_string_quote_metachars (const std::string &s)
+{
+ if (s.find('"') == std::string::npos)
+ return s;
+
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+ for (size_t i = 0; i < s_size; i++)
+ {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '"')
+ {
+ output.push_back ('\\');
+ }
+ output.push_back (ch);
+ }
+ return output;
+}
+
+JSONString::JSONString () :
+ JSONValue(JSONValue::Kind::String),
+ m_data()
+{
+}
+
+JSONString::JSONString (const char* s) :
+ JSONValue(JSONValue::Kind::String),
+ m_data(s ? s : "")
+{
+}
+
+JSONString::JSONString (const std::string& s) :
+ JSONValue(JSONValue::Kind::String),
+ m_data(s)
+{
+}
+
+void
+JSONString::Write (std::ostream& s)
+{
+ s << "\"" << json_string_quote_metachars(m_data).c_str() <<"\"";
+}
+
+uint64_t
+JSONNumber::GetAsUnsigned() const
+{
+ switch (m_data_type)
+ {
+ case DataType::Unsigned:
+ return m_data.m_unsigned;
+ case DataType::Signed:
+ return (uint64_t)m_data.m_signed;
+ case DataType::Double:
+ return (uint64_t)m_data.m_double;
+ }
+ assert("Unhandled data type");
+}
+
+int64_t
+JSONNumber::GetAsSigned() const
+{
+ switch (m_data_type)
+ {
+ case DataType::Unsigned:
+ return (int64_t)m_data.m_unsigned;
+ case DataType::Signed:
+ return m_data.m_signed;
+ case DataType::Double:
+ return (int64_t)m_data.m_double;
+ }
+ assert("Unhandled data type");
+}
+
+double
+JSONNumber::GetAsDouble() const
+{
+ switch (m_data_type)
+ {
+ case DataType::Unsigned:
+ return (double)m_data.m_unsigned;
+ case DataType::Signed:
+ return (double)m_data.m_signed;
+ case DataType::Double:
+ return m_data.m_double;
+ }
+ assert("Unhandled data type");
+}
+
+void
+JSONNumber::Write (std::ostream& s)
+{
+ switch (m_data_type)
+ {
+ case DataType::Unsigned:
+ s << m_data.m_unsigned;
+ break;
+ case DataType::Signed:
+ s << m_data.m_signed;
+ break;
+ case DataType::Double:
+ // Set max precision to emulate %g.
+ s << std::setprecision(std::numeric_limits<double>::digits10 + 1);
+ s << m_data.m_double;
+ break;
+ }
+}
+
+JSONTrue::JSONTrue () :
+ JSONValue(JSONValue::Kind::True)
+{
+}
+
+void
+JSONTrue::Write(std::ostream& s)
+{
+ s << "true";
+}
+
+JSONFalse::JSONFalse () :
+ JSONValue(JSONValue::Kind::False)
+{
+}
+
+void
+JSONFalse::Write(std::ostream& s)
+{
+ s << "false";
+}
+
+JSONNull::JSONNull () :
+ JSONValue(JSONValue::Kind::Null)
+{
+}
+
+void
+JSONNull::Write(std::ostream& s)
+{
+ s << "null";
+}
+
+JSONObject::JSONObject () :
+ JSONValue(JSONValue::Kind::Object)
+{
+}
+
+void
+JSONObject::Write (std::ostream& s)
+{
+ bool first = true;
+ s << '{';
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (;iter != end; iter++)
+ {
+ if (first)
+ first = false;
+ else
+ s << ',';
+ JSONString key(iter->first);
+ JSONValue::SP value(iter->second);
+ key.Write(s);
+ s << ':';
+ value->Write(s);
+ }
+ s << '}';
+}
+
+bool
+JSONObject::SetObject (const std::string& key,
+ JSONValue::SP value)
+{
+ if (key.empty() || nullptr == value.get())
+ return false;
+ m_elements[key] = value;
+ return true;
+}
+
+JSONValue::SP
+JSONObject::GetObject (const std::string& key) const
+{
+ auto iter = m_elements.find(key), end = m_elements.end();
+ if (iter == end)
+ return JSONValue::SP();
+ return iter->second;
+}
+
+bool
+JSONObject::GetObjectAsBool (const std::string& key, bool& value) const
+{
+ auto value_sp = GetObject(key);
+ if (!value_sp)
+ {
+ // The given key doesn't exist, so we have no value.
+ return false;
+ }
+
+ if (JSONTrue::classof(value_sp.get()))
+ {
+ // We have the value, and it is true.
+ value = true;
+ return true;
+ }
+ else if (JSONFalse::classof(value_sp.get()))
+ {
+ // We have the value, and it is false.
+ value = false;
+ return true;
+ }
+ else
+ {
+ // We don't have a valid bool value for the given key.
+ return false;
+ }
+}
+
+bool
+JSONObject::GetObjectAsString (const std::string& key, std::string& value) const
+{
+ auto value_sp = GetObject(key);
+ if (!value_sp)
+ {
+ // The given key doesn't exist, so we have no value.
+ return false;
+ }
+
+ if (!JSONString::classof(value_sp.get()))
+ return false;
+
+ value = static_cast<JSONString*>(value_sp.get())->GetData();
+ return true;
+}
+
+JSONArray::JSONArray () :
+ JSONValue(JSONValue::Kind::Array)
+{
+}
+
+void
+JSONArray::Write (std::ostream& s)
+{
+ bool first = true;
+ s << '[';
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (;iter != end; iter++)
+ {
+ if (first)
+ first = false;
+ else
+ s << ',';
+ (*iter)->Write(s);
+ }
+ s << ']';
+}
+
+bool
+JSONArray::SetObject (Index i,
+ JSONValue::SP value)
+{
+ if (value.get() == nullptr)
+ return false;
+ if (i < m_elements.size())
+ {
+ m_elements[i] = value;
+ return true;
+ }
+ if (i == m_elements.size())
+ {
+ m_elements.push_back(value);
+ return true;
+ }
+ return false;
+}
+
+bool
+JSONArray::AppendObject (JSONValue::SP value)
+{
+ if (value.get() == nullptr)
+ return false;
+ m_elements.push_back(value);
+ return true;
+}
+
+JSONValue::SP
+JSONArray::GetObject (Index i)
+{
+ if (i < m_elements.size())
+ return m_elements[i];
+ return JSONValue::SP();
+}
+
+JSONArray::Size
+JSONArray::GetNumElements ()
+{
+ return m_elements.size();
+}
+
+
+JSONParser::JSONParser (const char *cstr) :
+ StringExtractor(cstr)
+{
+}
+
+JSONParser::Token
+JSONParser::GetToken (std::string &value)
+{
+ std::ostringstream error;
+
+ value.clear();
+ SkipSpaces ();
+ const uint64_t start_index = m_index;
+ const char ch = GetChar();
+ switch (ch)
+ {
+ case '{': return Token::ObjectStart;
+ case '}': return Token::ObjectEnd;
+ case '[': return Token::ArrayStart;
+ case ']': return Token::ArrayEnd;
+ case ',': return Token::Comma;
+ case ':': return Token::Colon;
+ case '\0': return Token::EndOfFile;
+ case 't':
+ if (GetChar() == 'r')
+ if (GetChar() == 'u')
+ if (GetChar() == 'e')
+ return Token::True;
+ break;
+
+ case 'f':
+ if (GetChar() == 'a')
+ if (GetChar() == 'l')
+ if (GetChar() == 's')
+ if (GetChar() == 'e')
+ return Token::False;
+ break;
+
+ case 'n':
+ if (GetChar() == 'u')
+ if (GetChar() == 'l')
+ if (GetChar() == 'l')
+ return Token::Null;
+ break;
+
+ case '"':
+ {
+ while (1)
+ {
+ bool was_escaped = false;
+ int escaped_ch = GetEscapedChar(was_escaped);
+ if (escaped_ch == -1)
+ {
+ error << "error: an error occurred getting a character from offset " <<start_index;
+ value = std::move(error.str());
+ return Token::Error;
+
+ }
+ else
+ {
+ const bool is_end_quote = escaped_ch == '"';
+ const bool is_null = escaped_ch == 0;
+ if (was_escaped || (!is_end_quote && !is_null))
+ {
+ if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX)
+ {
+ value.append(1, (char)escaped_ch);
+ }
+ else
+ {
+ error << "error: wide character support is needed for unicode character 0x" << std::setprecision(4) << std::hex << escaped_ch;
+ error << " at offset " << start_index;
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ }
+ else if (is_end_quote)
+ {
+ return Token::String;
+ }
+ else if (is_null)
+ {
+ value = "error: missing end quote for string";
+ return Token::Error;
+ }
+ }
+ }
+ }
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ bool done = false;
+ bool got_decimal_point = false;
+ uint64_t exp_index = 0;
+ bool got_int_digits = (ch >= '0') && (ch <= '9');
+ bool got_frac_digits = false;
+ bool got_exp_digits = false;
+ while (!done)
+ {
+ const char next_ch = PeekChar();
+ switch (next_ch)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (exp_index != 0)
+ {
+ got_exp_digits = true;
+ }
+ else if (got_decimal_point)
+ {
+ got_frac_digits = true;
+ }
+ else
+ {
+ got_int_digits = true;
+ }
+ ++m_index; // Skip this character
+ break;
+
+ case '.':
+ if (got_decimal_point)
+ {
+ error << "error: extra decimal point found at offset " << start_index;
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ else
+ {
+ got_decimal_point = true;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ if (exp_index != 0)
+ {
+ error << "error: extra exponent character found at offset " << start_index;
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ else
+ {
+ exp_index = m_index;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case '+':
+ case '-':
+ // The '+' and '-' can only come after an exponent character...
+ if (exp_index == m_index - 1)
+ {
+ ++m_index; // Skip the exponent sign character
+ }
+ else
+ {
+ error << "error: unexpected " << next_ch << " character at offset " << start_index;
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+
+ if (m_index > start_index)
+ {
+ value = m_packet.substr(start_index, m_index - start_index);
+ if (got_decimal_point)
+ {
+ if (exp_index != 0)
+ {
+ // We have an exponent, make sure we got exponent digits
+ if (got_exp_digits)
+ {
+ return Token::Float;
+ }
+ else
+ {
+ error << "error: got exponent character but no exponent digits at offset in float value \"" << value.c_str() << "\"";
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ }
+ else
+ {
+ // No exponent, but we need at least one decimal after the decimal point
+ if (got_frac_digits)
+ {
+ return Token::Float;
+ }
+ else
+ {
+ error << "error: no digits after decimal point \"" << value.c_str() << "\"";
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ }
+ }
+ else
+ {
+ // No decimal point
+ if (got_int_digits)
+ {
+ // We need at least some integer digits to make an integer
+ return Token::Integer;
+ }
+ else
+ {
+ error << "error: no digits negate sign \"" << value.c_str() << "\"";
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ }
+ }
+ else
+ {
+ error << "error: invalid number found at offset " << start_index;
+ value = std::move(error.str());
+ return Token::Error;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ error << "error: failed to parse token at offset " << start_index << " (around character '" << ch << "')";
+ value = std::move(error.str());
+ return Token::Error;
+}
+
+int
+JSONParser::GetEscapedChar(bool &was_escaped)
+{
+ was_escaped = false;
+ const char ch = GetChar();
+ if (ch == '\\')
+ {
+ was_escaped = true;
+ const char ch2 = GetChar();
+ switch (ch2)
+ {
+ case '"':
+ case '\\':
+ case '/':
+ default:
+ break;
+
+ case 'b': return '\b';
+ case 'f': return '\f';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ case 't': return '\t';
+ case 'u':
+ {
+ const int hi_byte = DecodeHexU8();
+ const int lo_byte = DecodeHexU8();
+ if (hi_byte >=0 && lo_byte >= 0)
+ return hi_byte << 8 | lo_byte;
+ return -1;
+ }
+ break;
+ }
+ return ch2;
+ }
+ return ch;
+}
+
+JSONValue::SP
+JSONParser::ParseJSONObject ()
+{
+ // The "JSONParser::Token::ObjectStart" token should have already been consumed
+ // by the time this function is called
+ std::unique_ptr<JSONObject> dict_up(new JSONObject());
+
+ std::string value;
+ std::string key;
+ while (1)
+ {
+ JSONParser::Token token = GetToken(value);
+
+ if (token == JSONParser::Token::String)
+ {
+ key.swap(value);
+ token = GetToken(value);
+ if (token == JSONParser::Token::Colon)
+ {
+ JSONValue::SP value_sp = ParseJSONValue();
+ if (value_sp)
+ dict_up->SetObject(key, value_sp);
+ else
+ break;
+ }
+ }
+ else if (token == JSONParser::Token::ObjectEnd)
+ {
+ return JSONValue::SP(dict_up.release());
+ }
+ else if (token == JSONParser::Token::Comma)
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP
+JSONParser::ParseJSONArray ()
+{
+ // The "JSONParser::Token::ObjectStart" token should have already been consumed
+ // by the time this function is called
+ std::unique_ptr<JSONArray> array_up(new JSONArray());
+
+ std::string value;
+ std::string key;
+ while (1)
+ {
+ JSONValue::SP value_sp = ParseJSONValue();
+ if (value_sp)
+ array_up->AppendObject(value_sp);
+ else
+ break;
+
+ JSONParser::Token token = GetToken(value);
+ if (token == JSONParser::Token::Comma)
+ {
+ continue;
+ }
+ else if (token == JSONParser::Token::ArrayEnd)
+ {
+ return JSONValue::SP(array_up.release());
+ }
+ else
+ {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP
+JSONParser::ParseJSONValue ()
+{
+ std::string value;
+ const JSONParser::Token token = GetToken(value);
+ switch (token)
+ {
+ case JSONParser::Token::ObjectStart:
+ return ParseJSONObject();
+
+ case JSONParser::Token::ArrayStart:
+ return ParseJSONArray();
+
+ case JSONParser::Token::Integer:
+ {
+ if (value.front() == '-')
+ {
+ bool success = false;
+ int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(sval));
+ }
+ else
+ {
+ bool success = false;
+ uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(uval));
+ }
+ }
+ break;
+
+ case JSONParser::Token::Float:
+ {
+ bool success = false;
+ double val = StringConvert::ToDouble(value.c_str(), 0.0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(val));
+ }
+ break;
+
+ case JSONParser::Token::String:
+ return JSONValue::SP(new JSONString(value));
+
+ case JSONParser::Token::True:
+ return JSONValue::SP(new JSONTrue());
+
+ case JSONParser::Token::False:
+ return JSONValue::SP(new JSONFalse());
+
+ case JSONParser::Token::Null:
+ return JSONValue::SP(new JSONNull());
+
+ default:
+ break;
+ }
+ return JSONValue::SP();
+
+}
diff --git a/lldb/tools/debugserver/source/JSON.h b/lldb/tools/debugserver/source/JSON.h
new file mode 100644
index 00000000000..d1723d87a38
--- /dev/null
+++ b/lldb/tools/debugserver/source/JSON.h
@@ -0,0 +1,382 @@
+//===---------------------JSON.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_JSON_h_
+#define utility_JSON_h_
+
+// This cross-project usage is fine as StringExtractor.h is entirely
+// self-contained.
+#include "lldb/Utility/StringExtractor.h"
+
+// C includes
+#include <inttypes.h>
+#include <stdint.h>
+
+// C++ includes
+#include <map>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+class JSONValue
+{
+public:
+ virtual void
+ Write (std::ostream& s) = 0;
+
+ typedef std::shared_ptr<JSONValue> SP;
+
+ enum class Kind
+ {
+ String,
+ Number,
+ True,
+ False,
+ Null,
+ Object,
+ Array
+ };
+
+ JSONValue (Kind k) :
+ m_kind(k)
+ {}
+
+ Kind
+ GetKind() const
+ {
+ return m_kind;
+ }
+
+ virtual
+ ~JSONValue () = default;
+
+private:
+ const Kind m_kind;
+};
+
+class JSONString : public JSONValue
+{
+public:
+ JSONString ();
+ JSONString (const char* s);
+ JSONString (const std::string& s);
+
+ JSONString (const JSONString& s) = delete;
+ JSONString&
+ operator = (const JSONString& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONString> SP;
+
+ std::string
+ GetData () { return m_data; }
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::String;
+ }
+
+ ~JSONString() override = default;
+
+private:
+
+ static std::string
+ json_string_quote_metachars (const std::string&);
+
+ std::string m_data;
+};
+
+class JSONNumber : public JSONValue
+{
+public:
+ typedef std::shared_ptr<JSONNumber> SP;
+
+ // We cretae a constructor for all integer and floating point type with using templates and
+ // SFINAE to avoid having ambiguous overloads because of the implicit type promotion. If we
+ // would have constructors only with int64_t, uint64_t and double types then constructing a
+ // JSONNumber from an int32_t (or any other similar type) would fail to compile.
+
+ template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_unsigned<T>::value>::type* = nullptr>
+ explicit JSONNumber (T u) :
+ JSONValue(JSONValue::Kind::Number),
+ m_data_type(DataType::Unsigned)
+ {
+ m_data.m_unsigned = u;
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_signed<T>::value>::type* = nullptr>
+ explicit JSONNumber (T s) :
+ JSONValue(JSONValue::Kind::Number),
+ m_data_type(DataType::Signed)
+ {
+ m_data.m_signed = s;
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+ explicit JSONNumber (T d) :
+ JSONValue(JSONValue::Kind::Number),
+ m_data_type(DataType::Double)
+ {
+ m_data.m_double = d;
+ }
+
+ ~JSONNumber() override = default;
+
+ JSONNumber (const JSONNumber& s) = delete;
+ JSONNumber&
+ operator = (const JSONNumber& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ uint64_t
+ GetAsUnsigned() const;
+
+ int64_t
+ GetAsSigned() const;
+
+ double
+ GetAsDouble() const;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::Number;
+ }
+
+private:
+ enum class DataType : uint8_t
+ {
+ Unsigned,
+ Signed,
+ Double
+ } m_data_type;
+
+ union
+ {
+ uint64_t m_unsigned;
+ int64_t m_signed;
+ double m_double;
+ } m_data;
+};
+
+class JSONTrue : public JSONValue
+{
+public:
+ JSONTrue ();
+
+ JSONTrue (const JSONTrue& s) = delete;
+ JSONTrue&
+ operator = (const JSONTrue& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONTrue> SP;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::True;
+ }
+
+ ~JSONTrue() override = default;
+};
+
+class JSONFalse : public JSONValue
+{
+public:
+ JSONFalse ();
+
+ JSONFalse (const JSONFalse& s) = delete;
+ JSONFalse&
+ operator = (const JSONFalse& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONFalse> SP;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::False;
+ }
+
+ ~JSONFalse() override = default;
+};
+
+class JSONNull : public JSONValue
+{
+public:
+ JSONNull ();
+
+ JSONNull (const JSONNull& s) = delete;
+ JSONNull&
+ operator = (const JSONNull& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONNull> SP;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::Null;
+ }
+
+ ~JSONNull() override = default;
+};
+
+class JSONObject : public JSONValue
+{
+public:
+ JSONObject ();
+
+ JSONObject (const JSONObject& s) = delete;
+ JSONObject&
+ operator = (const JSONObject& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONObject> SP;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::Object;
+ }
+
+ bool
+ SetObject (const std::string& key,
+ JSONValue::SP value);
+
+ JSONValue::SP
+ GetObject (const std::string& key) const;
+
+ // -------------------------------------------------------------------------
+ /// Return keyed value as bool
+ ///
+ /// @param[in] key
+ /// The value of the key to lookup
+ ///
+ /// @param[out] value
+ /// The value of the key as a bool. Undefined if the key doesn't
+ /// exist or if the key is not either true or false.
+ ///
+ /// @return
+ /// true if the key existed as was a bool value; false otherwise.
+ /// Note the return value is *not* the value of the bool, use
+ /// \b value for that.
+ // -------------------------------------------------------------------------
+ bool
+ GetObjectAsBool (const std::string& key, bool& value) const;
+
+ bool
+ GetObjectAsString (const std::string& key, std::string& value) const;
+
+ ~JSONObject() override = default;
+
+private:
+ typedef std::map<std::string, JSONValue::SP> Map;
+ typedef Map::iterator Iterator;
+ Map m_elements;
+};
+
+class JSONArray : public JSONValue
+{
+public:
+ JSONArray ();
+
+ JSONArray (const JSONArray& s) = delete;
+ JSONArray&
+ operator = (const JSONArray& s) = delete;
+
+ void
+ Write(std::ostream& s) override;
+
+ typedef std::shared_ptr<JSONArray> SP;
+
+ static bool classof(const JSONValue *V)
+ {
+ return V->GetKind() == JSONValue::Kind::Array;
+ }
+
+private:
+ typedef std::vector<JSONValue::SP> Vector;
+ typedef Vector::iterator Iterator;
+ typedef Vector::size_type Index;
+ typedef Vector::size_type Size;
+
+public:
+ bool
+ SetObject (Index i,
+ JSONValue::SP value);
+
+ bool
+ AppendObject (JSONValue::SP value);
+
+ JSONValue::SP
+ GetObject (Index i);
+
+ Size
+ GetNumElements ();
+
+ ~JSONArray() override = default;
+
+ Vector m_elements;
+};
+
+class JSONParser : public StringExtractor
+{
+public:
+ enum Token
+ {
+ Invalid,
+ Error,
+ ObjectStart,
+ ObjectEnd,
+ ArrayStart,
+ ArrayEnd,
+ Comma,
+ Colon,
+ String,
+ Integer,
+ Float,
+ True,
+ False,
+ Null,
+ EndOfFile
+ };
+
+ JSONParser (const char *cstr);
+
+ int
+ GetEscapedChar (bool &was_escaped);
+
+ Token
+ GetToken (std::string &value);
+
+ JSONValue::SP
+ ParseJSONValue ();
+
+protected:
+ JSONValue::SP
+ ParseJSONObject ();
+
+ JSONValue::SP
+ ParseJSONArray ();
+};
+
+#endif // utility_JSON_h_
diff --git a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt
index a154dcc485a..49b0931ee59 100644
--- a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt
+++ b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt
@@ -3,6 +3,7 @@
add_subdirectory(i386)
#add_subdirectory(ppc)
add_subdirectory(x86_64)
+add_subdirectory(DarwinLog)
include_directories(..)
@@ -32,6 +33,7 @@ set(DEBUGSERVER_USED_LIBS
lldbUtility
lldbDebugserverMacOSX_I386
lldbDebugserverMacOSX_X86_64
+ lldbDebugserverMacOSX_DarwinLog
)
add_lldb_executable(debugserver
@@ -46,6 +48,7 @@ add_lldb_executable(debugserver
MachThreadList.cpp
MachVMMemory.cpp
MachVMRegion.cpp
+ OsLogger.cpp
${generated_mach_interfaces}
${DEBUGSERVER_VERS_GENERATED_FILE}
)
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp
new file mode 100644
index 00000000000..e98a131512a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp
@@ -0,0 +1,18 @@
+//===-- ActivityStore.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ActivityStore.h"
+
+ActivityStore::ActivityStore()
+{
+}
+
+ActivityStore::~ActivityStore()
+{
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h
new file mode 100644
index 00000000000..2e998ba367c
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h
@@ -0,0 +1,36 @@
+//===-- ActivityStore.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ActivityStore_h
+#define ActivityStore_h
+
+#include <string>
+
+#include "ActivityStreamSPI.h"
+
+class ActivityStore
+{
+public:
+
+ virtual
+ ~ActivityStore();
+
+ virtual const char*
+ GetActivityForID(os_activity_id_t activity_id) const = 0;
+
+ virtual std::string
+ GetActivityChainForID(os_activity_id_t activity_id) const = 0;
+
+protected:
+
+ ActivityStore();
+
+};
+
+#endif /* ActivityStore_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h
new file mode 100644
index 00000000000..4ddf13b3fcd
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h
@@ -0,0 +1,200 @@
+//===-- ActivityStreamAPI.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ActivityStreamSPI_h
+#define ActivityStreamSPI_h
+
+#include <sys/time.h>
+#include <xpc/xpc.h>
+
+#define OS_ACTIVITY_MAX_CALLSTACK 32
+
+// Enums
+
+enum
+{
+ OS_ACTIVITY_STREAM_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_STREAM_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_STREAM_PAYLOAD = 0x00000004,
+ OS_ACTIVITY_STREAM_HISTORICAL = 0x00000008,
+ OS_ACTIVITY_STREAM_CALLSTACK = 0x00000010,
+ OS_ACTIVITY_STREAM_DEBUG = 0x00000020,
+ OS_ACTIVITY_STREAM_BUFFERED = 0x00000040,
+ OS_ACTIVITY_STREAM_NO_SENSITIVE = 0x00000080,
+ OS_ACTIVITY_STREAM_INFO = 0x00000100,
+ OS_ACTIVITY_STREAM_PROMISCUOUS = 0x00000200,
+ OS_ACTIVITY_STREAM_PRECISE_TIMESTAMPS = 0x00000200
+};
+typedef uint32_t os_activity_stream_flag_t;
+
+enum
+{
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE = 0x0201,
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION = 0x0202,
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_USERACTION = 0x0203,
+
+ OS_ACTIVITY_STREAM_TYPE_TRACE_MESSAGE = 0x0300,
+
+ OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE = 0x0400,
+ OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE = 0x0480,
+
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_BEGIN = 0x0601,
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_END = 0x0602,
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_EVENT = 0x0603,
+
+ OS_ACTIVITY_STREAM_TYPE_STATEDUMP_EVENT = 0x0A00,
+};
+typedef uint32_t os_activity_stream_type_t;
+
+enum
+{
+ OS_ACTIVITY_STREAM_EVENT_STARTED = 1,
+ OS_ACTIVITY_STREAM_EVENT_STOPPED = 2,
+ OS_ACTIVITY_STREAM_EVENT_FAILED = 3,
+ OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED = 4,
+ OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED = 5,
+};
+typedef uint32_t os_activity_stream_event_t;
+
+// Types
+
+typedef uint64_t os_activity_id_t;
+typedef struct os_activity_stream_s *os_activity_stream_t;
+typedef struct os_activity_stream_entry_s *os_activity_stream_entry_t;
+
+#define OS_ACTIVITY_STREAM_COMMON() \
+ uint64_t trace_id; \
+ uint64_t timestamp; \
+ uint64_t thread; \
+ const uint8_t *image_uuid; \
+ const char *image_path; \
+ struct timeval tv_gmt; \
+ struct timezone tz; \
+ uint32_t offset \
+
+
+typedef struct os_activity_stream_common_s {
+ OS_ACTIVITY_STREAM_COMMON();
+} *os_activity_stream_common_t;
+
+struct os_activity_create_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *name;
+ os_activity_id_t creator_aid;
+ uint64_t unique_pid;
+};
+
+struct os_activity_transition_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ os_activity_id_t transition_id;
+};
+
+typedef struct os_log_message_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const uint8_t *buffer;
+ size_t buffer_sz;
+ const uint8_t *privdata;
+ size_t privdata_sz;
+ const char *subsystem;
+ const char *category;
+ uint32_t oversize_id;
+ uint8_t ttl;
+ bool persisted;
+} *os_log_message_t;
+
+typedef struct os_trace_message_v2_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const void *buffer;
+ size_t bufferLen;
+ xpc_object_t __unsafe_unretained payload;
+} *os_trace_message_v2_t;
+
+typedef struct os_activity_useraction_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *action;
+ bool persisted;
+} *os_activity_useraction_t;
+
+typedef struct os_signpost_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const uint8_t *buffer;
+ size_t buffer_sz;
+ const uint8_t *privdata;
+ size_t privdata_sz;
+ const char *subsystem;
+ const char *category;
+ uint64_t duration_nsec;
+ uint32_t callstack_depth;
+ uint64_t callstack[OS_ACTIVITY_MAX_CALLSTACK];
+} *os_signpost_t;
+
+typedef struct os_activity_statedump_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ char *message;
+ size_t message_size;
+ char image_path_buffer[PATH_MAX];
+} *os_activity_statedump_t;
+
+struct os_activity_stream_entry_s {
+ os_activity_stream_type_t type;
+
+ // information about the process streaming the data
+ pid_t pid;
+ uint64_t proc_id;
+ const uint8_t *proc_imageuuid;
+ const char *proc_imagepath;
+
+ // the activity associated with this streamed event
+ os_activity_id_t activity_id;
+ os_activity_id_t parent_id;
+
+ union {
+ struct os_activity_stream_common_s common;
+ struct os_activity_create_s activity_create;
+ struct os_activity_transition_s activity_transition;
+ struct os_log_message_s log_message;
+ struct os_trace_message_v2_s trace_message;
+ struct os_activity_useraction_s useraction;
+ struct os_signpost_s signpost;
+ struct os_activity_statedump_s statedump;
+ };
+};
+
+// Blocks
+
+typedef bool (^os_activity_stream_block_t)(os_activity_stream_entry_t entry,
+ int error);
+
+typedef void (^os_activity_stream_event_block_t)(
+ os_activity_stream_t stream,
+ os_activity_stream_event_t event);
+
+// SPI entry point prototypes
+
+typedef os_activity_stream_t
+ (*os_activity_stream_for_pid_t)(pid_t pid, os_activity_stream_flag_t flags,
+ os_activity_stream_block_t stream_block);
+
+typedef void
+(*os_activity_stream_resume_t)(os_activity_stream_t stream);
+
+typedef void
+ (*os_activity_stream_cancel_t)(os_activity_stream_t stream);
+
+typedef char *
+ (*os_log_copy_formatted_message_t)(os_log_message_t log_message);
+
+typedef void
+ (*os_activity_stream_set_event_handler_t)
+ (os_activity_stream_t stream, os_activity_stream_event_block_t block);
+
+#endif /* ActivityStreamSPI_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt b/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt
new file mode 100644
index 00000000000..47e7362f0d5
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Due to sources including headers like:
+# #include "MacOSX/i386/DNBArchImplI386.h"
+# we must include the grandparent directory...
+include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source)
+
+add_library(lldbDebugserverMacOSX_DarwinLog
+ ActivityStore.cpp
+ DarwinLogCollector.cpp
+ LogFilter.cpp
+ LogFilterChain.cpp
+ LogFilterExactMatch.cpp
+ LogFilterRegex.cpp
+ LogMessage.cpp
+ LogMessageOsLog.cpp
+ )
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp
new file mode 100644
index 00000000000..30f3e521669
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp
@@ -0,0 +1,835 @@
+//===-- DarwinLogCollector.cpp ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DarwinLogCollector.h"
+#include "ActivityStreamSPI.h"
+
+#include <dlfcn.h>
+
+#include <cinttypes>
+#include <mutex>
+#include <vector>
+
+#include "DarwinLogTypes.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "LogFilterChain.h"
+#include "LogFilterExactMatch.h"
+#include "LogFilterRegex.h"
+#include "LogMessageOsLog.h"
+#include "MachProcess.h"
+#include "RNBContext.h"
+#include "RNBDefs.h"
+#include "RNBRemote.h"
+
+// Use an anonymous namespace for variables and methods that have no
+// reason to leak out through the interface.
+namespace
+{
+ /// Specify max depth that the activity parent-child chain will search
+ /// back to get the full activity chain name. If we do more than this,
+ /// we assume either we hit a loop or it's just too long.
+ static const size_t MAX_ACTIVITY_CHAIN_DEPTH = 10;
+
+ // Used to tap into and retrieve logs from target process.
+ // (Consumer of os_log).
+ static os_activity_stream_for_pid_t s_os_activity_stream_for_pid;
+ static os_activity_stream_resume_t s_os_activity_stream_resume;
+ static os_activity_stream_cancel_t s_os_activity_stream_cancel;
+ static os_log_copy_formatted_message_t s_os_log_copy_formatted_message;
+ static os_activity_stream_set_event_handler_t
+ s_os_activity_stream_set_event_handler;
+
+ bool
+ LookupSPICalls()
+ {
+ static std::once_flag s_once_flag;
+ static bool s_has_spi;
+
+ std::call_once(s_once_flag, [] {
+ s_os_activity_stream_for_pid = (os_activity_stream_for_pid_t)
+ dlsym(RTLD_DEFAULT, "os_activity_stream_for_pid");
+ s_os_activity_stream_resume = (os_activity_stream_resume_t)
+ dlsym(RTLD_DEFAULT, "os_activity_stream_resume");
+ s_os_activity_stream_cancel = (os_activity_stream_cancel_t)
+ dlsym(RTLD_DEFAULT, "os_activity_stream_cancel");
+ s_os_log_copy_formatted_message = (os_log_copy_formatted_message_t)
+ dlsym(RTLD_DEFAULT, "os_log_copy_formatted_message");
+ s_os_activity_stream_set_event_handler =
+ (os_activity_stream_set_event_handler_t)
+ dlsym(RTLD_DEFAULT, "os_activity_stream_set_event_handler");
+
+ // We'll indicate we're all set if every function entry point
+ // was found.
+ s_has_spi =
+ (s_os_activity_stream_for_pid != nullptr) &&
+ (s_os_activity_stream_resume != nullptr) &&
+ (s_os_activity_stream_cancel != nullptr) &&
+ (s_os_log_copy_formatted_message != nullptr) &&
+ (s_os_activity_stream_set_event_handler != nullptr);
+ if (s_has_spi)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Found os_log SPI calls.");
+ // Tell LogMessageOsLog how to format messages when search
+ // criteria requires it.
+ LogMessageOsLog::SetFormatterFunction(
+ s_os_log_copy_formatted_message);
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Failed to find os_log SPI "
+ "calls.");
+ }
+ });
+
+ return s_has_spi;
+ }
+
+ using Mutex = std::mutex;
+ static Mutex s_collector_mutex;
+ static std::vector<DarwinLogCollectorSP> s_collectors;
+
+ static void
+ TrackCollector(const DarwinLogCollectorSP &collector_sp)
+ {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ if (std::find(s_collectors.begin(), s_collectors.end(), collector_sp)
+ != s_collectors.end())
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "attempted to add same collector multiple times");
+ return;
+ }
+ s_collectors.push_back(collector_sp);
+ }
+
+ static void
+ StopTrackingCollector(const DarwinLogCollectorSP &collector_sp)
+ {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ s_collectors.erase(std::remove(s_collectors.begin(), s_collectors.end(),
+ collector_sp),
+ s_collectors.end());
+ }
+
+ static DarwinLogCollectorSP
+ FindCollectorForProcess(pid_t pid)
+ {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ for (const auto &collector_sp : s_collectors)
+ {
+ if (collector_sp && (collector_sp->GetProcessID() == pid))
+ return collector_sp;
+ }
+ return DarwinLogCollectorSP();
+ }
+
+ static FilterTarget
+ TargetStringToEnum(const std::string &filter_target_name)
+ {
+ if (filter_target_name == "activity")
+ return eFilterTargetActivity;
+ else if (filter_target_name == "activity-chain")
+ return eFilterTargetActivityChain;
+ else if (filter_target_name == "category")
+ return eFilterTargetCategory;
+ else if (filter_target_name == "message")
+ return eFilterTargetMessage;
+ else if (filter_target_name == "subsystem")
+ return eFilterTargetSubsystem;
+ else
+ return eFilterTargetInvalid;
+ }
+
+ class Configuration
+ {
+ public:
+
+ Configuration(const JSONObject &config) :
+ m_is_valid(false),
+ m_activity_stream_flags(OS_ACTIVITY_STREAM_PROCESS_ONLY),
+ m_filter_chain_sp(nullptr)
+ {
+ // Parse out activity stream flags
+ if (!ParseSourceFlags(config))
+ {
+ m_is_valid = false;
+ return;
+ }
+
+ // Parse filter rules
+ if (!ParseFilterRules(config))
+ {
+ m_is_valid = false;
+ return;
+ }
+
+ // Everything worked.
+ m_is_valid = true;
+ }
+
+ bool
+ ParseSourceFlags(const JSONObject &config)
+ {
+ // Get the source-flags dictionary.
+ auto source_flags_sp = config.GetObject("source-flags");
+ if (!source_flags_sp)
+ return false;
+ if (!JSONObject::classof(source_flags_sp.get()))
+ return false;
+
+ const JSONObject &source_flags =
+ *static_cast<JSONObject*>(source_flags_sp.get());
+
+ // Parse out the flags.
+ bool include_any_process = false;
+ bool include_callstacks = false;
+ bool include_info_level = false;
+ bool include_debug_level = false;
+ bool live_stream = false;
+
+ if (!source_flags.GetObjectAsBool("any-process",
+ include_any_process))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Source-flag 'any-process' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("callstacks",
+ include_callstacks))
+ {
+ // We currently suppress the availability of this on the lldb
+ // side. We include here for devices when we enable in the
+ // future.
+ // DNBLogThreadedIf(LOG_DARWIN_LOG,
+ // "Source-flag 'callstacks' missing from "
+ // "configuration.");
+
+ // OK. We just skip callstacks.
+ // return false;
+ }
+ if (!source_flags.GetObjectAsBool("info-level",
+ include_info_level))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Source-flag 'info-level' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("debug-level",
+ include_debug_level))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Source-flag 'debug-level' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("live-stream",
+ live_stream))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Source-flag 'live-stream' missing from "
+ "configuration.");
+ return false;
+ }
+
+ // Setup the SPI flags based on this.
+ m_activity_stream_flags = 0;
+ if (!include_any_process)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY;
+ if (include_callstacks)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_CALLSTACK;
+ if (include_info_level)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_INFO;
+ if (include_debug_level)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_DEBUG;
+ if (!live_stream)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_BUFFERED;
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "m_activity_stream_flags = 0x%03x",
+ m_activity_stream_flags);
+
+ return true;
+ }
+
+ bool
+ ParseFilterRules(const JSONObject &config)
+ {
+ // Retrieve the default rule.
+ bool filter_default_accept = true;
+ if (!config.GetObjectAsBool("filter-fall-through-accepts",
+ filter_default_accept))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Setting 'filter-fall-through-accepts' "
+ "missing from configuration.");
+ return false;
+ }
+ m_filter_chain_sp.reset(new LogFilterChain(filter_default_accept));
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "DarwinLog no-match rule: %s.",
+ filter_default_accept ? "accept" : "reject");
+
+ // If we don't have the filter-rules array, we're done.
+ auto filter_rules_sp = config.GetObject("filter-rules");
+ if (!filter_rules_sp)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "No 'filter-rules' config element, all log "
+ "entries will use the no-match action (%s).",
+ filter_default_accept ? "accept" : "reject");
+ return true;
+ }
+ if (!JSONArray::classof(filter_rules_sp.get()))
+ return false;
+ const JSONArray &rules_config =
+ *static_cast<JSONArray*>(filter_rules_sp.get());
+
+ // Create the filters.
+ for (auto &rule_sp : rules_config.m_elements)
+ {
+ if (!JSONObject::classof(rule_sp.get()))
+ return false;
+ const JSONObject &rule_config = *static_cast<JSONObject*>
+ (rule_sp.get());
+
+ // Get whether this filter accepts or rejects.
+ bool filter_accepts = true;
+ if (!rule_config.GetObjectAsBool("accept", filter_accepts))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Filter 'accept' element missing.");
+ return false;
+ }
+
+ // Grab the target log field attribute for the match.
+ std::string target_attribute;
+ if (!rule_config.GetObjectAsString("attribute",
+ target_attribute))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Filter 'attribute' element missing.");
+ return false;
+ }
+ auto target_enum = TargetStringToEnum(target_attribute);
+ if (target_enum == eFilterTargetInvalid)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Filter attribute '%s' unsupported.",
+ target_attribute.c_str());
+ return false;
+ }
+
+ // Handle operation-specific fields and filter creation.
+ std::string filter_type;
+ if (!rule_config.GetObjectAsString("type", filter_type))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Filter 'type' element missing.");
+ return false;
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "Reading filter of type '%s'", filter_type
+ .c_str());
+
+ LogFilterSP filter_sp;
+ if (filter_type == "regex")
+ {
+ // Grab the regex for the match.
+ std::string regex;
+ if (!rule_config.GetObjectAsString("regex",
+ regex))
+ {
+ DNBLogError("Regex filter missing 'regex' element.");
+ return false;
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "regex for filter: \"%s\"", regex.c_str());
+
+ // Create the regex filter.
+ auto regex_filter =
+ new LogFilterRegex(filter_accepts, target_enum, regex);
+ filter_sp.reset(regex_filter);
+
+ // Validate that the filter is okay.
+ if (!regex_filter->IsValid())
+ {
+ DNBLogError("Invalid regex in filter: "
+ "regex=\"%s\", error=%s",
+ regex.c_str(),
+ regex_filter->GetErrorAsCString());
+ return false;
+ }
+ }
+ else if (filter_type == "match")
+ {
+ // Grab the regex for the match.
+ std::string exact_text;
+ if (!rule_config.GetObjectAsString("exact_text",
+ exact_text))
+ {
+ DNBLogError("Exact match filter missing "
+ "'exact_text' element.");
+ return false;
+ }
+
+ // Create the filter.
+ filter_sp.reset(new LogFilterExactMatch(filter_accepts,
+ target_enum,
+ exact_text));
+ }
+
+ // Add the filter to the chain.
+ m_filter_chain_sp->AppendFilter(filter_sp);
+ }
+ return true;
+ }
+
+ bool
+ IsValid() const
+ {
+ return m_is_valid;
+ }
+
+ os_activity_stream_flag_t
+ GetActivityStreamFlags() const
+ {
+ return m_activity_stream_flags;
+ }
+
+ const LogFilterChainSP &
+ GetLogFilterChain() const
+ {
+ return m_filter_chain_sp;
+ }
+
+ private:
+
+ bool m_is_valid;
+ os_activity_stream_flag_t m_activity_stream_flags;
+ LogFilterChainSP m_filter_chain_sp;
+
+ };
+}
+
+bool
+DarwinLogCollector::IsSupported()
+{
+ // We're supported if we have successfully looked up the SPI entry points.
+ return LookupSPICalls();
+}
+
+bool
+DarwinLogCollector::StartCollectingForProcess(nub_process_t pid,
+ const JSONObject &config)
+{
+ // If we're currently collecting for this process, kill the existing
+ // collector.
+ if (CancelStreamForProcess(pid))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() killed existing DarwinLog collector for pid %d.",
+ __FUNCTION__, pid);
+ }
+
+ // If the process isn't alive, we're done.
+ if (!DNBProcessIsAlive(pid))
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() cannot collect for pid %d: process not alive.",
+ __FUNCTION__, pid);
+ return false;
+ }
+
+ // Validate the configuration.
+ auto spi_config = Configuration(config);
+ if (!spi_config.IsValid())
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() invalid configuration, will not enable log "
+ "collection", __FUNCTION__);
+ return false;
+ }
+
+ // Create the stream collector that will manage collected data
+ // for this pid.
+ DarwinLogCollectorSP collector_sp(new DarwinLogCollector(pid,
+ spi_config.GetLogFilterChain()));
+ std::weak_ptr<DarwinLogCollector> collector_wp(collector_sp);
+
+ // Setup the stream handling block.
+ os_activity_stream_block_t block = ^bool (os_activity_stream_entry_t entry,
+ int error) {
+ // Check if our collector is still alive.
+ DarwinLogCollectorSP inner_collector_sp = collector_wp.lock();
+ if (!inner_collector_sp)
+ return false;
+ return inner_collector_sp->HandleStreamEntry(entry, error);
+ };
+
+ os_activity_stream_event_block_t stream_event_block =
+ ^void (os_activity_stream_t stream, os_activity_stream_event_t event) {
+ switch (event)
+ {
+ case OS_ACTIVITY_STREAM_EVENT_STARTED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_STARTED, stream %p.",
+ (void*)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_STOPPED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_STOPPED, stream %p.",
+ (void*)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_FAILED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_FAILED, stream %p.",
+ (void*)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED, stream %p.",
+ (void*)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED, stream %p.",
+ (void*)stream);
+ break;
+ }
+ };
+
+ // Create the stream.
+ os_activity_stream_t activity_stream =
+ (*s_os_activity_stream_for_pid)(pid,
+ spi_config.GetActivityStreamFlags(),
+ block);
+ collector_sp->SetActivityStream(activity_stream);
+
+ // Specify the stream-related event handler.
+ (*s_os_activity_stream_set_event_handler)(
+ activity_stream, stream_event_block);
+
+ // Start the stream.
+ (*s_os_activity_stream_resume)(activity_stream);
+
+ TrackCollector(collector_sp);
+ return true;
+}
+
+DarwinLogEventVector
+DarwinLogCollector::GetEventsForProcess(nub_process_t pid)
+{
+ auto collector_sp = FindCollectorForProcess(pid);
+ if (!collector_sp)
+ {
+ // We're not tracking a stream for this process.
+ return DarwinLogEventVector();
+ }
+
+ return collector_sp->RemoveEvents();
+}
+
+bool
+DarwinLogCollector::CancelStreamForProcess(nub_process_t pid)
+{
+ auto collector_sp = FindCollectorForProcess(pid);
+ if (!collector_sp)
+ {
+ // We're not tracking a stream for this process.
+ return false;
+ }
+
+ collector_sp->CancelActivityStream();
+ StopTrackingCollector(collector_sp);
+
+ return true;
+}
+
+const char*
+DarwinLogCollector::GetActivityForID(os_activity_id_t activity_id) const
+{
+ auto find_it = m_activity_map.find(activity_id);
+ return (find_it != m_activity_map.end()) ?
+ find_it->second.m_name.c_str() :
+ nullptr;
+}
+
+/// Retrieve the full parent-child chain for activity names. These
+/// can be arbitrarily deep. This method assumes the caller has already
+/// locked the activity mutex.
+void
+DarwinLogCollector::GetActivityChainForID_internal(os_activity_id_t activity_id,
+ std::string &result,
+ size_t depth) const
+{
+ if (depth > MAX_ACTIVITY_CHAIN_DEPTH)
+ {
+ // Terminating condition - too deeply nested.
+ return;
+ }
+ else if (activity_id == 0)
+ {
+ // Terminating condition - no activity.
+ return;
+ }
+
+ auto find_it = m_activity_map.find(activity_id);
+ if (find_it == m_activity_map.end())
+ {
+ //Terminating condition - no data for activity_id.
+ return;
+ }
+
+ // Activity name becomes parent activity name chain + ':' + our activity
+ // name.
+ GetActivityChainForID_internal(find_it->second.m_parent_id, result,
+ depth + 1);
+ if (!result.empty())
+ result += ':';
+ result += find_it->second.m_name;
+}
+
+std::string
+DarwinLogCollector::GetActivityChainForID(os_activity_id_t activity_id) const
+{
+ std::string result;
+ {
+ std::lock_guard<std::mutex> locker(m_activity_info_mutex);
+ GetActivityChainForID_internal(activity_id, result, 1);
+ }
+ return result;
+}
+
+DarwinLogCollector::DarwinLogCollector(nub_process_t pid,
+ const LogFilterChainSP &filter_chain_sp):
+ ActivityStore(),
+ m_pid(pid),
+ m_activity_stream(0),
+ m_events(),
+ m_events_mutex(),
+ m_filter_chain_sp(filter_chain_sp),
+ m_activity_info_mutex(),
+ m_activity_map()
+{
+}
+
+DarwinLogCollector::~DarwinLogCollector()
+{
+ // Cancel the stream.
+ if (m_activity_stream)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "tearing down activity stream "
+ "collector for %d", m_pid);
+ (*s_os_activity_stream_cancel)(m_activity_stream);
+ m_activity_stream = 0;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "no stream to tear down for %d",
+ m_pid);
+ }
+}
+
+void
+DarwinLogCollector::SignalDataAvailable()
+{
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (!remoteSP)
+ {
+ // We're done. This is unexpected.
+ StopTrackingCollector(shared_from_this());
+ return;
+ }
+
+ RNBContext& ctx = remoteSP->Context();
+ ctx.Events().SetEvents(RNBContext::event_darwin_log_data_available);
+ // Wait for the main thread to consume this notification if it requested
+ // we wait for it.
+ ctx.Events().WaitForResetAck(RNBContext::event_darwin_log_data_available);
+}
+
+void
+DarwinLogCollector::SetActivityStream(os_activity_stream_t activity_stream)
+{
+ m_activity_stream = activity_stream;
+}
+
+bool
+DarwinLogCollector::HandleStreamEntry(os_activity_stream_entry_t entry,
+ int error)
+{
+ if ((error == 0) && (entry != nullptr))
+ {
+ if (entry->pid != m_pid)
+ {
+ // For now, skip messages not originating from our process.
+ // Later we might want to keep all messages related to an event
+ // that we're tracking, even when it came from another process,
+ // possibly doing work on our behalf.
+ return true;
+ }
+
+ switch (entry->type)
+ {
+ case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received activity create: "
+ "%s, creator aid %" PRIu64 ", unique_pid %" PRIu64
+ "(activity id=%" PRIu64 ", parent id=%" PRIu64 ")",
+ entry->activity_create.name,
+ entry->activity_create.creator_aid,
+ entry->activity_create.unique_pid, entry->activity_id,
+ entry->parent_id
+ );
+ {
+ std::lock_guard<std::mutex> locker(m_activity_info_mutex);
+ m_activity_map.insert(std::make_pair(
+ entry->activity_id,
+ ActivityInfo(
+ entry->activity_create.name,
+ entry->activity_id,
+ entry->parent_id)));
+ }
+ break;
+
+ case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION:
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received activity transition:"
+ "new aid: %" PRIu64 "(activity id=%" PRIu64
+ ", parent id=%" PRIu64 ", tid %" PRIu64 ")",
+ entry->activity_transition.transition_id,
+ entry->activity_id, entry->parent_id,
+ entry->activity_transition.thread);
+ break;
+
+ case OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE:
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "received log message: "
+ "(activity id=%" PRIu64 ", parent id=%" PRIu64 ", "
+ "tid %" PRIu64 "): format %s",
+ entry->activity_id, entry->parent_id,
+ entry->log_message.thread,
+ entry->log_message.format ? entry->log_message.format :
+ "<invalid-format>");
+
+ // Do the real work here.
+ {
+ // Ensure our process is still alive. If not, we can
+ // cancel the collection.
+ if (!DNBProcessIsAlive(m_pid))
+ {
+ // We're outta here. This is the manner in which we
+ // stop collecting for a process.
+ StopTrackingCollector(shared_from_this());
+ return false;
+ }
+
+ LogMessageOsLog os_log_message(*this, *entry);
+ if (!m_filter_chain_sp ||
+ !m_filter_chain_sp->GetAcceptMessage(os_log_message))
+ {
+ // This log message was rejected by the filter,
+ // so stop processing it now.
+ return true;
+ }
+
+ // Copy over the relevant bits from the message.
+ const struct os_log_message_s &log_message =
+ entry->log_message;
+
+ DarwinLogEventSP message_sp(new DarwinLogEvent());
+ // Indicate this event is a log message event.
+ message_sp->AddStringItem("type", "log");
+
+ // Add the message contents (fully expanded).
+ // Consider expanding on the remote side.
+ // Then we don't pay for expansion until when it is
+ // used.
+ const char *message_text = os_log_message.GetMessage();
+ if (message_text)
+ message_sp->AddStringItem("message", message_text);
+
+ // Add some useful data fields.
+ message_sp->AddIntegerItem("timestamp",
+ log_message.timestamp);
+
+ // Do we want to do all activity name resolution on this
+ // side? Maybe. For now, send IDs and ID->name mappings
+ // and fix this up on that side. Later, when we add
+ // debugserver-side filtering, we'll want to get the
+ // activity names over here, so we should probably
+ // just send them as resolved strings.
+ message_sp->AddIntegerItem("activity_id",
+ entry->activity_id);
+ message_sp->AddIntegerItem("parent_id",
+ entry->parent_id);
+ message_sp->AddIntegerItem("thread_id",
+ log_message.thread);
+ if (log_message.subsystem && strlen(log_message.subsystem)
+ > 0)
+ message_sp->AddStringItem("subsystem",
+ log_message.subsystem);
+ if (log_message.category && strlen(log_message.category)
+ > 0)
+ message_sp->AddStringItem("category",
+ log_message.category);
+ if (entry->activity_id != 0)
+ {
+ std::string activity_chain =
+ GetActivityChainForID(entry->activity_id);
+ if (!activity_chain.empty())
+ message_sp->AddStringItem("activity-chain",
+ activity_chain);
+ }
+
+ // Add it to the list for later collection.
+ {
+ std::lock_guard<std::mutex> locker(m_events_mutex);
+ m_events.push_back(message_sp);
+ }
+ SignalDataAvailable();
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "HandleStreamEntry: final call, "
+ "error %d", error);
+ }
+ return true;
+}
+
+DarwinLogEventVector
+DarwinLogCollector::RemoveEvents()
+{
+ DarwinLogEventVector returned_events;
+ {
+ std::lock_guard<std::mutex> locker(m_events_mutex);
+ returned_events.swap(m_events);
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): removing %lu "
+ "queued log entries", __FUNCTION__,
+ returned_events.size());
+ return returned_events;
+}
+
+void
+DarwinLogCollector::CancelActivityStream()
+{
+ if (!m_activity_stream)
+ return;
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): canceling "
+ "activity stream %p", __FUNCTION__,
+ m_activity_stream);
+ (*s_os_activity_stream_cancel)(m_activity_stream);
+ m_activity_stream = nullptr;
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h
new file mode 100644
index 00000000000..8263423fce1
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h
@@ -0,0 +1,139 @@
+//===-- DarwinLogCollector.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogCollector_h
+#define DarwinLogCollector_h
+
+#include <sys/types.h>
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "ActivityStore.h"
+#include "ActivityStreamSPI.h"
+#include "DarwinLogEvent.h"
+#include "DarwinLogInterfaces.h"
+#include "DNBDefs.h"
+#include "JSON.h"
+
+class DarwinLogCollector;
+typedef std::shared_ptr<DarwinLogCollector> DarwinLogCollectorSP;
+
+class DarwinLogCollector:
+ public std::enable_shared_from_this<DarwinLogCollector>,
+ public ActivityStore
+{
+public:
+
+ //------------------------------------------------------------------
+ /// Return whether the os_log and activity tracing SPI is available.
+ ///
+ /// @return \b true if the activity stream support is available,
+ /// \b false otherwise.
+ //------------------------------------------------------------------
+ static bool
+ IsSupported();
+
+ //------------------------------------------------------------------
+ /// Return a log function suitable for DNBLog to use as the internal
+ /// logging function.
+ ///
+ /// @return a DNBLog-style logging function if IsSupported() returns
+ /// true; otherwise, returns nullptr.
+ //------------------------------------------------------------------
+ static DNBCallbackLog
+ GetLogFunction();
+
+ static bool
+ StartCollectingForProcess(nub_process_t pid, const JSONObject &config);
+
+ static bool
+ CancelStreamForProcess(nub_process_t pid);
+
+ static DarwinLogEventVector
+ GetEventsForProcess(nub_process_t pid);
+
+ ~DarwinLogCollector();
+
+ pid_t
+ GetProcessID() const
+ {
+ return m_pid;
+ }
+
+ //------------------------------------------------------------------
+ // ActivityStore API
+ //------------------------------------------------------------------
+ const char*
+ GetActivityForID(os_activity_id_t activity_id) const override;
+
+ std::string
+ GetActivityChainForID(os_activity_id_t activity_id) const override;
+
+
+private:
+
+ DarwinLogCollector() = delete;
+ DarwinLogCollector(const DarwinLogCollector&) = delete;
+ DarwinLogCollector &operator=(const DarwinLogCollector&) = delete;
+
+ explicit
+ DarwinLogCollector(nub_process_t pid,
+ const LogFilterChainSP &filter_chain_sp);
+
+ void
+ SignalDataAvailable();
+
+ void
+ SetActivityStream(os_activity_stream_t activity_stream);
+
+ bool
+ HandleStreamEntry(os_activity_stream_entry_t entry, int error);
+
+ DarwinLogEventVector
+ RemoveEvents();
+
+ void
+ CancelActivityStream();
+
+ void
+ GetActivityChainForID_internal(os_activity_id_t activity_id,
+ std::string &result, size_t depth) const;
+
+ struct ActivityInfo
+ {
+ ActivityInfo(const char *name, os_activity_id_t activity_id,
+ os_activity_id_t parent_activity_id) :
+ m_name(name),
+ m_id(activity_id),
+ m_parent_id(parent_activity_id)
+ {
+ }
+
+ const std::string m_name;
+ const os_activity_id_t m_id;
+ const os_activity_id_t m_parent_id;
+ };
+
+ using ActivityMap = std::unordered_map<os_activity_id_t, ActivityInfo>;
+
+ const nub_process_t m_pid;
+ os_activity_stream_t m_activity_stream;
+ DarwinLogEventVector m_events;
+ std::mutex m_events_mutex;
+ LogFilterChainSP m_filter_chain_sp;
+
+ /// Mutex to protect activity info (activity name and parent structures)
+ mutable std::mutex m_activity_info_mutex;
+ /// Map of activity id to ActivityInfo
+ ActivityMap m_activity_map;
+};
+
+#endif /* LogStreamCollector_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h
new file mode 100644
index 00000000000..6be3b81f3e1
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h
@@ -0,0 +1,27 @@
+//===-- DarwinLogEvent.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogEvent_h
+#define DarwinLogEvent_h
+
+#include <memory>
+#include <vector>
+
+#include "JSONGenerator.h"
+
+// =============================================================================
+/// Each discrete unit of information is described as an event, such as
+/// the emission of a single log message.
+// =============================================================================
+
+using DarwinLogEvent = JSONGenerator::Dictionary;
+using DarwinLogEventSP = std::shared_ptr<DarwinLogEvent>;
+using DarwinLogEventVector = std::vector<DarwinLogEventSP>;
+
+#endif
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h
new file mode 100644
index 00000000000..afb979f6cfe
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h
@@ -0,0 +1,25 @@
+//===-- DarwinLogInterfaces.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogInterfaces_h
+#define DarwinLogInterfaces_h
+
+#include <memory>
+
+class ActivityStore;
+
+class LogFilter;
+using LogFilterSP = std::shared_ptr<LogFilter>;
+
+class LogFilterChain;
+using LogFilterChainSP = std::shared_ptr<LogFilterChain>;
+
+class LogMessage;
+
+#endif /* DarwinLogInterfaces_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h
new file mode 100644
index 00000000000..a090fba0c14
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h
@@ -0,0 +1,23 @@
+//===-- DarwinLogTypes.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogTypes_h
+#define DarwinLogTypes_h
+
+enum FilterTarget
+{
+ eFilterTargetInvalid,
+ eFilterTargetActivity,
+ eFilterTargetActivityChain,
+ eFilterTargetCategory,
+ eFilterTargetMessage,
+ eFilterTargetSubsystem
+};
+
+#endif /* DarwinLogTypes_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp
new file mode 100644
index 00000000000..80af86ec505
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp
@@ -0,0 +1,14 @@
+//===-- LogFilter.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilter.h"
+
+LogFilter::~LogFilter()
+{
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h
new file mode 100644
index 00000000000..4d54394cb32
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h
@@ -0,0 +1,44 @@
+//===-- LogFilter.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilter_h
+#define LogFilter_h
+
+#include "DarwinLogInterfaces.h"
+
+class LogFilter
+{
+public:
+
+ virtual
+ ~LogFilter();
+
+ virtual bool
+ DoesMatch(const LogMessage &message) const = 0;
+
+ bool
+ MatchesAreAccepted() const
+ {
+ return m_matches_accept;
+ }
+
+protected:
+
+ LogFilter(bool matches_accept) :
+ m_matches_accept(matches_accept)
+ {
+ }
+
+private:
+
+ bool m_matches_accept;
+
+};
+
+#endif /* LogFilter_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp
new file mode 100644
index 00000000000..888fbd9bfb1
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp
@@ -0,0 +1,61 @@
+//===-- LogFilterChain.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilterChain.h"
+
+#include "LogFilter.h"
+
+LogFilterChain::LogFilterChain(bool default_accept) :
+ m_filters(),
+ m_default_accept(default_accept)
+{
+}
+
+void
+LogFilterChain::AppendFilter(const LogFilterSP &filter_sp)
+{
+ if (filter_sp)
+ m_filters.push_back(filter_sp);
+}
+
+void
+LogFilterChain::ClearFilterChain()
+{
+ m_filters.clear();
+}
+
+bool
+LogFilterChain::GetDefaultAccepts() const
+{
+ return m_default_accept;
+}
+
+void
+LogFilterChain::SetDefaultAccepts(bool default_accept)
+{
+ m_default_accept = default_accept;
+}
+
+bool
+LogFilterChain::GetAcceptMessage(const LogMessage &message) const
+{
+ for (auto filter_sp : m_filters)
+ {
+ if (filter_sp->DoesMatch(message))
+ {
+ // This message matches this filter. If the filter accepts matches,
+ // this message matches; otherwise, it rejects matches.
+ return filter_sp->MatchesAreAccepted();
+ }
+ }
+
+ // None of the filters matched. Therefore, we do whatever the
+ // default fall-through rule says.
+ return m_default_accept;
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h
new file mode 100644
index 00000000000..8774c15d98c
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h
@@ -0,0 +1,48 @@
+//===-- LogFilterChain.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LogFilterChain_h
+#define LogFilterChain_h
+
+#include <vector>
+
+#include "DarwinLogInterfaces.h"
+
+class LogFilterChain
+{
+public:
+
+ LogFilterChain(bool default_accept);
+
+ void
+ AppendFilter(const LogFilterSP &filter_sp);
+
+ void
+ ClearFilterChain();
+
+ bool
+ GetDefaultAccepts() const;
+
+ void
+ SetDefaultAccepts(bool default_accepts);
+
+ bool
+ GetAcceptMessage(const LogMessage &message) const;
+
+private:
+
+ using FilterVector = std::vector<LogFilterSP>;
+
+ FilterVector m_filters;
+ bool m_default_accept;
+
+};
+
+#endif /* LogFilterChain_hpp */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp
new file mode 100644
index 00000000000..b85458618fd
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp
@@ -0,0 +1,57 @@
+//===-- LogFilterExactMatch.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilterExactMatch.h"
+#include "LogMessage.h"
+
+LogFilterExactMatch::LogFilterExactMatch(bool match_accepts,
+ FilterTarget filter_target,
+ const std::string &match_text) :
+ LogFilter(match_accepts),
+ m_filter_target(filter_target),
+ m_match_text(match_text)
+{
+}
+
+bool
+LogFilterExactMatch::DoesMatch(const LogMessage &message) const
+{
+ switch (m_filter_target)
+ {
+ case eFilterTargetActivity:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return m_match_text == message.GetActivity();
+ case eFilterTargetActivityChain:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return m_match_text == message.GetActivityChain();
+ case eFilterTargetCategory:
+ // Empty fields never match a condition.
+ if (!message.HasCategory())
+ return false;
+ return m_match_text == message.GetCategory();
+ case eFilterTargetMessage:
+ {
+ const char *message_text = message.GetMessage();
+ return (message_text != nullptr) &&
+ (m_match_text == message_text);
+ }
+ case eFilterTargetSubsystem:
+ // Empty fields never match a condition.
+ if (!message.HasSubsystem())
+ return false;
+ return m_match_text == message.GetSubsystem();
+ default:
+ // We don't know this type.
+ return false;
+ }
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h
new file mode 100644
index 00000000000..131afaf234b
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h
@@ -0,0 +1,36 @@
+//===-- LogFilterExactMatch.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilterExactMatch_h
+#define LogFilterExactMatch_h
+
+#include <string>
+
+#include "DarwinLogInterfaces.h"
+#include "DarwinLogTypes.h"
+#include "LogFilter.h"
+
+class LogFilterExactMatch : public LogFilter
+{
+public:
+
+ LogFilterExactMatch(bool match_accepts, FilterTarget filter_target,
+ const std::string &match_text);
+
+ bool
+ DoesMatch(const LogMessage &message) const override;
+
+private:
+
+ const FilterTarget m_filter_target;
+ const std::string m_match_text;
+
+};
+
+#endif
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp
new file mode 100644
index 00000000000..128c7514493
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp
@@ -0,0 +1,118 @@
+//===-- LogFilterRegex.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "LogFilterRegex.h"
+
+#include "DNBLog.h"
+#include "LogMessage.h"
+
+//----------------------------------------------------------------------
+// Enable enhanced mode if it is available. This allows for things like
+// \d for digit, \s for space, and many more, but it isn't available
+// everywhere.
+//----------------------------------------------------------------------
+#if defined(REG_ENHANCED)
+#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED|REG_EXTENDED)
+#else
+#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED)
+#endif
+
+LogFilterRegex::LogFilterRegex(bool match_accepts,
+ FilterTarget filter_target,
+ const std::string &regex) :
+ LogFilter(match_accepts),
+ m_filter_target(filter_target),
+ m_regex_text(regex),
+ m_regex(),
+ m_is_valid(false),
+ m_error_text()
+{
+ // Clear it.
+ memset(&m_regex, 0, sizeof(m_regex));
+
+ // Compile it.
+ if (!regex.empty())
+ {
+ auto comp_err = ::regcomp(&m_regex, regex.c_str(),
+ DEFAULT_COMPILE_FLAGS);
+ m_is_valid = (comp_err == 0);
+ if (!m_is_valid)
+ {
+ char buffer[256];
+ buffer[0] = '\0';
+ ::regerror(comp_err, &m_regex, buffer, sizeof(buffer));
+ m_error_text = buffer;
+ }
+ }
+}
+
+LogFilterRegex::~LogFilterRegex()
+{
+ if (m_is_valid)
+ {
+ // Free the regex internals.
+ regfree(&m_regex);
+ }
+}
+
+bool
+LogFilterRegex::DoesMatch(const LogMessage &message) const
+{
+ switch (m_filter_target)
+ {
+ case eFilterTargetActivity:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return ::regexec(&m_regex, message.GetActivity(), 0, nullptr, 0)
+ == 0;
+ case eFilterTargetActivityChain:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return ::regexec(&m_regex, message.GetActivityChain().c_str(), 0,
+ nullptr, 0) == 0;
+ case eFilterTargetCategory:
+ // Empty fields never match a condition.
+ if (!message.HasCategory())
+ return false;
+ return ::regexec(&m_regex, message.GetCategory(), 0, nullptr,
+ 0) == 0;
+ case eFilterTargetMessage:
+ {
+ const char *message_text = message.GetMessage();
+ if (!message_text)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "LogFilterRegex: regex "
+ "\"%s\" no match due to nullptr message.",
+ m_regex_text.c_str());
+ return false;
+ }
+
+ bool match = ::regexec(&m_regex, message_text, 0,
+ nullptr, 0) == 0;
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "LogFilterRegex: regex "
+ "\"%s\" %s message \"%s\".",
+ m_regex_text.c_str(),
+ match ? "matches" : "does not match",
+ message_text);
+ return match;
+ }
+ case eFilterTargetSubsystem:
+ // Empty fields never match a condition.
+ if (!message.HasSubsystem())
+ return false;
+ return ::regexec(&m_regex, message.GetSubsystem(), 0, nullptr,
+ 0) == 0;
+ default:
+ // We don't know this type.
+ return false;
+ }
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h
new file mode 100644
index 00000000000..796a62500ff
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h
@@ -0,0 +1,58 @@
+//===-- LogFilterRegex.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LogFilterRegex_h
+#define LogFilterRegex_h
+
+// C includes
+#include <regex.h>
+
+// C++ includes
+#include <string>
+
+#include "DarwinLogInterfaces.h"
+#include "DarwinLogTypes.h"
+#include "LogFilter.h"
+
+class LogFilterRegex : public LogFilter
+{
+public:
+
+ LogFilterRegex(bool match_accepts, FilterTarget filter_target,
+ const std::string &regex);
+
+ virtual
+ ~LogFilterRegex();
+
+ bool
+ IsValid() const
+ {
+ return m_is_valid;
+ }
+
+ const char*
+ GetErrorAsCString() const
+ {
+ return m_error_text.c_str();
+ }
+
+ bool
+ DoesMatch(const LogMessage &message) const override;
+
+private:
+
+ const FilterTarget m_filter_target;
+ const std::string m_regex_text;
+ regex_t m_regex;
+ bool m_is_valid;
+ std::string m_error_text;
+};
+
+#endif /* LogFilterSubsystemRegex_hpp */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp
new file mode 100644
index 00000000000..d98bb3de3c1
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp
@@ -0,0 +1,19 @@
+//===-- LogMessage.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "LogMessage.h"
+
+LogMessage::LogMessage()
+{
+}
+
+LogMessage::~LogMessage()
+{
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h
new file mode 100644
index 00000000000..0ec2b4277a4
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h
@@ -0,0 +1,53 @@
+//===-- LogMessage.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogMessage_h
+#define LogMessage_h
+
+#include <string>
+
+class LogMessage
+{
+public:
+
+ virtual
+ ~LogMessage();
+
+ virtual bool
+ HasActivity() const = 0;
+
+ virtual const char*
+ GetActivity() const = 0;
+
+ virtual std::string
+ GetActivityChain() const = 0;
+
+ virtual bool
+ HasCategory() const = 0;
+
+ virtual const char*
+ GetCategory() const = 0;
+
+ virtual bool
+ HasSubsystem() const = 0;
+
+ virtual const char*
+ GetSubsystem() const = 0;
+
+ // This can be expensive, so once we ask for it, we'll cache the result.
+ virtual const char*
+ GetMessage() const = 0;
+
+protected:
+
+ LogMessage();
+
+};
+
+#endif /* LogMessage_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp
new file mode 100644
index 00000000000..f3b6e443918
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp
@@ -0,0 +1,95 @@
+//===-- LogMessageOsLog.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogMessageOsLog.h"
+
+#include "ActivityStore.h"
+#include "ActivityStreamSPI.h"
+
+namespace
+{
+ static os_log_copy_formatted_message_t s_log_copy_formatted_message;
+}
+
+void
+LogMessageOsLog::SetFormatterFunction(os_log_copy_formatted_message_t
+ format_func)
+{
+ s_log_copy_formatted_message = format_func;
+}
+
+LogMessageOsLog::LogMessageOsLog(const ActivityStore &activity_store,
+ ActivityStreamEntry &entry) :
+ LogMessage(),
+ m_activity_store(activity_store),
+ m_entry(entry),
+ m_message()
+{
+}
+
+bool
+LogMessageOsLog::HasActivity() const
+{
+ return m_entry.activity_id != 0;
+}
+
+const char*
+LogMessageOsLog::GetActivity() const
+{
+ return m_activity_store.GetActivityForID(m_entry.activity_id);
+}
+
+std::string
+LogMessageOsLog::GetActivityChain() const
+{
+ return m_activity_store.GetActivityChainForID(m_entry.activity_id);
+}
+
+bool
+LogMessageOsLog::HasCategory() const
+{
+ return m_entry.log_message.category &&
+ (m_entry.log_message.category[0] != 0);
+}
+
+const char*
+LogMessageOsLog::GetCategory() const
+{
+ return m_entry.log_message.category;
+}
+
+bool
+LogMessageOsLog::HasSubsystem() const
+{
+ return m_entry.log_message.subsystem &&
+ (m_entry.log_message.subsystem[0] != 0);
+}
+
+const char*
+LogMessageOsLog::GetSubsystem() const
+{
+ return m_entry.log_message.subsystem;
+}
+
+const char*
+LogMessageOsLog::GetMessage() const
+{
+ if (m_message.empty())
+ {
+ std::unique_ptr<char[]> formatted_message(
+ s_log_copy_formatted_message(&m_entry.log_message));
+ if (formatted_message)
+ m_message = formatted_message.get();
+ // else
+ // TODO log
+ }
+
+ // This is safe to return as we're not modifying it once we've formatted it.
+ return m_message.c_str();
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h
new file mode 100644
index 00000000000..7c02059fc57
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h
@@ -0,0 +1,73 @@
+//===-- LogMessageOsLog.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LogMessageOsLog_h
+#define LogMessageOsLog_h
+
+#include "DarwinLogInterfaces.h"
+
+#include "ActivityStreamSPI.h"
+#include "LogMessage.h"
+
+using ActivityStreamEntry = struct os_activity_stream_entry_s;
+
+// -----------------------------------------------------------------------------
+/// Provides a unified wrapper around os_log()-style log messages.
+///
+/// The lifetime of this class is intended to be very short. The caller
+/// must ensure that the passed in ActivityStore and ActivityStreamEntry
+/// outlive this LogMessageOsLog entry.
+// -----------------------------------------------------------------------------
+
+class LogMessageOsLog : public LogMessage
+{
+public:
+
+ static void
+ SetFormatterFunction(os_log_copy_formatted_message_t format_func);
+
+ LogMessageOsLog(const ActivityStore &activity_store,
+ ActivityStreamEntry &entry);
+
+ // API methods
+
+ bool
+ HasActivity() const override;
+
+ const char*
+ GetActivity() const override;
+
+ std::string
+ GetActivityChain() const override;
+
+ bool
+ HasCategory() const override;
+
+ const char*
+ GetCategory() const override;
+
+ bool
+ HasSubsystem() const override;
+
+ const char*
+ GetSubsystem() const override;
+
+ const char*
+ GetMessage() const override;
+
+private:
+
+ const ActivityStore &m_activity_store;
+ ActivityStreamEntry &m_entry;
+ mutable std::string m_message;
+
+};
+
+#endif /* LogMessageOsLog_h */
diff --git a/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp b/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp
new file mode 100644
index 00000000000..1f424e7da17
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp
@@ -0,0 +1,71 @@
+//===-- OsLogger.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OsLogger.h"
+
+#if LLDB_USE_OS_LOG
+
+#include <os/log.h>
+
+#include "DNBDefs.h"
+#include "DNBLog.h"
+
+#define LLDB_OS_LOG_MAX_BUFFER_LENGTH 256
+
+namespace
+{
+ //----------------------------------------------------------------------
+ // Darwin os_log logging callback that can be registered with
+ // DNBLogSetLogCallback
+ //----------------------------------------------------------------------
+ void
+ DarwinLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args)
+ {
+ if (format == nullptr)
+ return;
+
+ static os_log_t g_logger;
+ if (!g_logger)
+ {
+ g_logger = os_log_create("com.apple.dt.lldb", "debugserver");
+ if (!g_logger)
+ return;
+ }
+
+ os_log_type_t log_type;
+ if (flags & DNBLOG_FLAG_FATAL) log_type = OS_LOG_TYPE_FAULT;
+ else if (flags & DNBLOG_FLAG_ERROR) log_type = OS_LOG_TYPE_ERROR;
+ else if (flags & DNBLOG_FLAG_WARNING) log_type = OS_LOG_TYPE_DEFAULT;
+ else if (flags & DNBLOG_FLAG_VERBOSE) log_type = OS_LOG_TYPE_DEBUG;
+ else log_type = OS_LOG_TYPE_DEFAULT;
+
+ // This code is unfortunate. os_log* only takes static strings, but
+ // our current log API isn't set up to make use of that style.
+ char buffer[LLDB_OS_LOG_MAX_BUFFER_LENGTH];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ os_log_with_type(g_logger, log_type, "%{public}s", buffer);
+ }
+}
+
+DNBCallbackLog
+OsLogger::GetLogFunction()
+{
+ return _os_log_impl ? DarwinLogCallback : nullptr;
+}
+
+#else
+
+DNBCallbackLog
+OsLogger::GetLogFunction()
+{
+ return nullptr;
+}
+
+#endif
diff --git a/lldb/tools/debugserver/source/MacOSX/OsLogger.h b/lldb/tools/debugserver/source/MacOSX/OsLogger.h
new file mode 100644
index 00000000000..6733b925335
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/OsLogger.h
@@ -0,0 +1,24 @@
+//===-- OsLogger.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OsLogger_h
+#define OsLogger_h
+
+#include "DNBDefs.h"
+
+class OsLogger
+{
+public:
+
+ static DNBCallbackLog
+ GetLogFunction();
+
+};
+
+#endif /* OsLogger_h */
diff --git a/lldb/tools/debugserver/source/RNBContext.cpp b/lldb/tools/debugserver/source/RNBContext.cpp
index 74ba5c45cb4..0b44fdbd581 100644
--- a/lldb/tools/debugserver/source/RNBContext.cpp
+++ b/lldb/tools/debugserver/source/RNBContext.cpp
@@ -245,6 +245,8 @@ RNBContext::EventsAsString (nub_event_t events, std::string& s)
s += "proc_stdio_available ";
if (events & event_proc_profile_data)
s += "proc_profile_data ";
+ if (events & event_darwin_log_data_available)
+ s += "darwin_log_data_available ";
if (events & event_read_packet_available)
s += "read_packet_available ";
if (events & event_read_thread_running)
diff --git a/lldb/tools/debugserver/source/RNBContext.h b/lldb/tools/debugserver/source/RNBContext.h
index 34fb9796ebe..83237ebccd9 100644
--- a/lldb/tools/debugserver/source/RNBContext.h
+++ b/lldb/tools/debugserver/source/RNBContext.h
@@ -25,21 +25,23 @@ class RNBContext
public:
enum
{
- event_proc_state_changed = 0x01,
- event_proc_thread_running = 0x02, // Sticky
- event_proc_thread_exiting = 0x04,
- event_proc_stdio_available = 0x08,
- event_proc_profile_data = 0x10,
- event_read_packet_available = 0x20,
- event_read_thread_running = 0x40, // Sticky
- event_read_thread_exiting = 0x80,
+ event_proc_state_changed = 0x001,
+ event_proc_thread_running = 0x002, // Sticky
+ event_proc_thread_exiting = 0x004,
+ event_proc_stdio_available = 0x008,
+ event_proc_profile_data = 0x010,
+ event_read_packet_available = 0x020,
+ event_read_thread_running = 0x040, // Sticky
+ event_read_thread_exiting = 0x080,
+ event_darwin_log_data_available = 0x100,
normal_event_bits = event_proc_state_changed |
event_proc_thread_exiting |
event_proc_stdio_available |
event_proc_profile_data |
event_read_packet_available |
- event_read_thread_exiting,
+ event_read_thread_exiting |
+ event_darwin_log_data_available,
sticky_event_bits = event_proc_thread_running |
event_read_thread_running,
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index 30b804316a1..11abe3c5d78 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -26,14 +26,18 @@
#include <sched.h>
#endif
+#include "DarwinLogCollector.h"
+#include "DarwinLogEvent.h"
#include "DNB.h"
#include "DNBDataRef.h"
#include "DNBLog.h"
#include "DNBThreadResumeActions.h"
#include "JSONGenerator.h"
+#include "OsLogger.h"
#include "RNBContext.h"
#include "RNBServices.h"
#include "RNBSocket.h"
+#include "JSON.h"
#include "lldb/Utility/StringExtractor.h"
#include "MacOSX/Genealogy.h"
#include "JSONGenerator.h"
@@ -52,6 +56,15 @@
#include <TargetConditionals.h> // for endianness predefines
//----------------------------------------------------------------------
+// constants
+//----------------------------------------------------------------------
+
+static const std::string OS_LOG_EVENTS_KEY_NAME("events");
+static const std::string JSON_ASYNC_TYPE_KEY_NAME("type");
+static const DarwinLogEventVector::size_type
+ DARWIN_LOG_MAX_EVENTS_PER_PACKET = 10;
+
+//----------------------------------------------------------------------
// std::iostream formatting macros
//----------------------------------------------------------------------
#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
@@ -77,6 +90,12 @@
#define INDENT_WITH_TABS(iword_idx) std::setfill('\t') << std::setw((iword_idx)) << ""
// Class to handle communications via gdb remote protocol.
+//----------------------------------------------------------------------
+// Prototypes
+//----------------------------------------------------------------------
+
+static std::string
+binary_encode_string (const std::string &s);
//----------------------------------------------------------------------
// Decode a single hex character and return the hex value as a number or
@@ -314,9 +333,10 @@ RNBRemote::CreatePacketTable ()
t.push_back (Packet (set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb."));
t.push_back (Packet (speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received."));
t.push_back (Packet (query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, "qXfer:", "Support the qXfer packet."));
+ t.push_back (Packet (query_supported_async_json_packets, &RNBRemote::HandlePacket_qStructuredDataPlugins, NULL, "qStructuredDataPlugins", "Query for the structured data plugins supported by the remote."));
+ t.push_back (Packet (configure_darwin_log, &RNBRemote::HandlePacket_QConfigureDarwinLog, NULL, "QConfigureDarwinLog:", "Configure the DarwinLog structured data plugin support."));
}
-
void
RNBRemote::FlushSTDIO ()
{
@@ -364,6 +384,86 @@ RNBRemote::SendAsyncProfileData ()
}
}
+void
+RNBRemote::SendAsyncDarwinLogData ()
+{
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): enter",
+ __FUNCTION__);
+
+ if (!m_ctx.HasValidProcessID())
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): ignoring due to"
+ "invalid process id", __FUNCTION__);
+ return;
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+ DarwinLogEventVector::size_type entry_count = 0;
+
+ // NOTE: the current looping structure here does nothing
+ // to guarantee that we can send off async packets faster
+ // than we generate them. It will keep sending as long
+ // as there's data to send.
+ do
+ {
+ DarwinLogEventVector events =
+ DNBProcessGetAvailableDarwinLogEvents(pid);
+ entry_count = events.size();
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop enter",
+ __FUNCTION__);
+
+ for (DarwinLogEventVector::size_type base_entry = 0;
+ base_entry < entry_count;
+ base_entry += DARWIN_LOG_MAX_EVENTS_PER_PACKET)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): inner loop enter",
+ __FUNCTION__);
+
+ // We limit the total number of entries we pack
+ // into a single JSON async packet just so it
+ // doesn't get too large.
+ JSONGenerator::Dictionary async_dictionary;
+
+ // Specify the type of the JSON async data we're sending.
+ async_dictionary.AddStringItem(
+ JSON_ASYNC_TYPE_KEY_NAME, "DarwinLog");
+
+ // Create an array entry in the dictionary to hold all
+ // the events going in this packet.
+ JSONGenerator::ArraySP events_array(new JSONGenerator::Array());
+ async_dictionary.AddItem(OS_LOG_EVENTS_KEY_NAME, events_array);
+
+ // We bundle up to DARWIN_LOG_MAX_EVENTS_PER_PACKET events in
+ // a single packet.
+ const auto inner_loop_bound =
+ std::min(base_entry + DARWIN_LOG_MAX_EVENTS_PER_PACKET,
+ entry_count);
+ for (DarwinLogEventVector::size_type i = base_entry;
+ i < inner_loop_bound; ++i)
+ {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): adding "
+ "entry index %lu to the JSON packet",
+ __FUNCTION__, i);
+ events_array->AddItem(events[i]);
+ }
+
+ // Send off the packet.
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): sending JSON "
+ "packet, %lu entries remain", __FUNCTION__,
+ entry_count - inner_loop_bound);
+ SendAsyncJSONPacket(async_dictionary);
+ }
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop exit",
+ __FUNCTION__);
+
+ } while (entry_count > 0);
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): exit",
+ __PRETTY_FUNCTION__);
+}
+
rnb_err_t
RNBRemote::SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer)
{
@@ -412,6 +512,19 @@ RNBRemote::SendAsyncProfileDataPacket (char *buf, nub_size_t buf_size)
return SendPacket(packet);
}
+rnb_err_t
+RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary)
+{
+ std::ostringstream stream;
+ // We're choosing something that is easy to spot if we somehow get one
+ // of these coming out at the wrong time (i.e. when the remote side
+ // is not waiting for a process control completion response).
+ stream << "JSON-async:";
+ dictionary.Dump(stream);
+ const std::string payload = binary_encode_string(stream.str());
+ return SendPacket(payload);
+}
+
// Given a std::string packet contents to send, possibly encode/compress it.
// If compression is enabled, the returned std::string will be in one of two
// forms:
@@ -1099,12 +1212,11 @@ decode_binary_data (const char *str, size_t len)
while (len--)
{
- unsigned char c = *str;
+ unsigned char c = *str++;
if (c == 0x7d && len > 0)
{
len--;
- str++;
- c = *str ^ 0x20;
+ c = *str++ ^ 0x20;
}
bytes.push_back (c);
}
@@ -1114,7 +1226,7 @@ decode_binary_data (const char *str, size_t len)
// Quote any meta characters in a std::string as per the binary
// packet convention in the gdb-remote protocol.
-std::string
+static std::string
binary_encode_string (const std::string &s)
{
std::string output;
@@ -2037,6 +2149,11 @@ set_logging (const char *p)
p += sizeof ("LOG_RNB_DEFAULT") - 1;
bitmask |= LOG_RNB_DEFAULT;
}
+ else if (strncmp (p, "LOG_DARWIN_LOG", sizeof ("LOG_DARWIN_LOG") - 1) == 0)
+ {
+ p += sizeof ("LOG_DARWIN_LOG") - 1;
+ bitmask |= LOG_DARWIN_LOG;
+ }
else if (strncmp (p, "LOG_RNB_NONE", sizeof ("LOG_RNB_NONE") - 1) == 0)
{
p += sizeof ("LOG_RNB_NONE") - 1;
@@ -2068,14 +2185,26 @@ set_logging (const char *p)
// Did we get a properly formatted logging bitmask?
if (p && *p == ';')
{
- // Enable DNB logging
- DNBLogSetLogCallback(ASLLogCallback, NULL);
+ // Enable DNB logging.
+ // Use the existing log callback if one was already configured.
+ if (!DNBLogGetLogCallback())
+ {
+ // Use the os_log()-based logger if available; otherwise,
+ // fallback to ASL.
+ auto log_callback = OsLogger::GetLogFunction();
+ if (log_callback)
+ DNBLogSetLogCallback(log_callback, nullptr);
+ else
+ DNBLogSetLogCallback(ASLLogCallback, nullptr);
+ }
+
+ // Update logging to use the configured log channel bitmask.
DNBLogSetLogMask (bitmask);
p++;
}
}
// We're not going to support logging to a file for now. All logging
- // goes through ASL.
+ // goes through ASL or the previously arranged log callback.
#if 0
else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0)
{
@@ -2306,6 +2435,99 @@ RNBRemote::HandlePacket_QSetDetachOnError (const char *p)
}
rnb_err_t
+RNBRemote::HandlePacket_qStructuredDataPlugins(const char *p)
+{
+ // We'll return a JSON array of supported packet types.
+ // The type is significant. For each of the supported
+ // packet types that have been enabled, there will be a
+ // 'J' async packet sent to the client with payload data.
+ // This payload data will be a JSON dictionary, and the
+ // top level dictionary will contain a string field with
+ // its value set to the relevant packet type from this list.
+ JSONGenerator::Array supported_json_packets;
+
+ // Check for DarwinLog (libtrace os_log/activity support).
+ if (DarwinLogCollector::IsSupported())
+ supported_json_packets.AddItem(JSONGenerator::StringSP(
+ new JSONGenerator::String("DarwinLog")));
+
+ // Send back the array.
+ std::ostringstream stream;
+ supported_json_packets.Dump(stream);
+ return SendPacket(stream.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QConfigureDarwinLog(const char *p)
+{
+ if (!DarwinLogCollector::IsSupported())
+ {
+ // We should never have been given this request.
+ return SendPacket ("E89");
+ }
+
+ // Ensure we have a process. We expect a separate configure request for
+ // each process launched/attached.
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("E94");
+
+ // Get the configuration dictionary.
+ p += strlen("QConfigureDarwinLog:");
+
+ // The configuration dictionary is binary encoded.
+ std::vector<uint8_t> unescaped_config_data = decode_binary_data(p, -1);
+ std::string unescaped_config_string((const char*)&unescaped_config_data[0],
+ unescaped_config_data.size());
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog: received config data: \"%s\"",
+ unescaped_config_string.c_str());
+ auto configuration_sp =
+ JSONParser(unescaped_config_string.c_str()).ParseJSONValue();
+ if (!configuration_sp)
+ {
+ // Malformed request - we require configuration data
+ // indicating whether we're enabling or disabling.
+ return SendPacket("E90");
+ }
+
+ if (!JSONObject::classof(configuration_sp.get()))
+ {
+ // Configuration data is not of the right type.
+ return SendPacket("E91");
+ }
+ JSONObject &config_dict = *static_cast<JSONObject*>(configuration_sp.get());
+
+ // Check if we're enabling or disabling.
+ auto enabled_sp = config_dict.GetObject("enabled");
+ if (!enabled_sp)
+ {
+ // Missing required "enabled" field.
+ return SendPacket("E92");
+ }
+ if (!JSONTrue::classof(enabled_sp.get()) &&
+ !JSONFalse::classof(enabled_sp.get()))
+ {
+ // Should be a boolean type, but wasn't.
+ return SendPacket("E93");
+ }
+ const bool enabling = JSONTrue::classof(enabled_sp.get());
+
+ // TODO - handle other configuration parameters here.
+
+ // Shut down any active activity stream for the process.
+ DarwinLogCollector::CancelStreamForProcess(pid);
+
+ if (enabling)
+ {
+ // Look up the procecess.
+ if (!DarwinLogCollector::StartCollectingForProcess(pid, config_dict))
+ return SendPacket("E95");
+ }
+
+ return SendPacket("OK");
+}
+
+rnb_err_t
RNBRemote::HandlePacket_QListThreadsInStopReply (const char *p)
{
// If this packet is received, it allows us to send an extra key/value
@@ -5510,26 +5732,33 @@ RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p)
if (need_vouchers_comma_sep)
json << ",";
need_vouchers_comma_sep = true;
- json << "\"process_infos\":[";
bool printed_one_process_info = false;
for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter)
{
if (printed_one_process_info)
json << ",";
- else
- printed_one_process_info = true;
Genealogy::ProcessExecutableInfoSP image_info_sp;
uint32_t idx = *iter;
image_info_sp = DNBGetGenealogyImageInfo (pid, idx);
- json << "{";
- char uuid_buf[37];
- uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf);
- json << "\"process_info_index\":" << idx << ",";
- json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\",";
- json << "\"image_uuid\":\"" << uuid_buf <<"\"";
- json << "}";
+ if (image_info_sp)
+ {
+ if (!printed_one_process_info)
+ {
+ json << "\"process_infos\":[";
+ printed_one_process_info = true;
+ }
+
+ json << "{";
+ char uuid_buf[37];
+ uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf);
+ json << "\"process_info_index\":" << idx << ",";
+ json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\",";
+ json << "\"image_uuid\":\"" << uuid_buf <<"\"";
+ json << "}";
+ }
}
- json << "]";
+ if (printed_one_process_info)
+ json << "]";
}
}
else
diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index 1bf7535e141..57c2a22e8df 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -135,6 +135,8 @@ public:
speed_test, // 'qSpeedTest:'
set_detach_on_error, // 'QSetDetachOnError:'
query_transfer, // 'qXfer:'
+ query_supported_async_json_packets, // 'QSupportedAsyncJSONPackets'
+ configure_darwin_log, // 'ConfigureDarwinLog:'
unknown_type
} PacketEnum;
@@ -251,6 +253,8 @@ public:
rnb_err_t HandlePacket_qXfer (const char *p);
rnb_err_t HandlePacket_stop_process (const char *p);
rnb_err_t HandlePacket_QSetDetachOnError (const char *p);
+ rnb_err_t HandlePacket_qStructuredDataPlugins (const char *p);
+ rnb_err_t HandlePacket_QConfigureDarwinLog (const char *p);
rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid);
rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer);
@@ -259,6 +263,8 @@ public:
void FlushSTDIO ();
void SendAsyncProfileData ();
rnb_err_t SendAsyncProfileDataPacket (char *buf, nub_size_t buf_size);
+ void SendAsyncDarwinLogData ();
+ rnb_err_t SendAsyncJSONPacket (const JSONGenerator::Dictionary &dictionary);
RNBContext& Context() { return m_ctx; }
RNBSocket& Comm() { return m_comm; }
diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp
index e9ddbf47faa..e1ee2c12131 100644
--- a/lldb/tools/debugserver/source/debugserver.cpp
+++ b/lldb/tools/debugserver/source/debugserver.cpp
@@ -34,6 +34,7 @@ extern "C" int proc_set_wakemon_params(pid_t, int, int); // <libproc_internal.h>
#include "DNB.h"
#include "DNBLog.h"
#include "DNBTimer.h"
+#include "OsLogger.h"
#include "PseudoTerminal.h"
#include "RNBContext.h"
#include "RNBServices.h"
@@ -524,6 +525,9 @@ RNBRunLoopInferiorExecuting (RNBRemote *remote)
// Clear some bits if we are not running so we don't send any async packets
event_mask &= ~RNBContext::event_proc_stdio_available;
event_mask &= ~RNBContext::event_proc_profile_data;
+ // When we enable async structured data packets over another logical channel,
+ // this can be relaxed.
+ event_mask &= ~RNBContext::event_darwin_log_data_available;
}
// We want to make sure we consume all process state changes and have
@@ -548,6 +552,11 @@ RNBRunLoopInferiorExecuting (RNBRemote *remote)
remote->SendAsyncProfileData();
}
+ if (set_events & RNBContext::event_darwin_log_data_available)
+ {
+ remote->SendAsyncDarwinLogData();
+ }
+
if (set_events & RNBContext::event_read_packet_available)
{
// handleReceivedPacket will take care of resetting the
@@ -1307,7 +1316,20 @@ main (int argc, char *argv[])
else
{
// Enable DNB logging
- DNBLogSetLogCallback(ASLLogCallback, NULL);
+
+ // if os_log() support is available, log through that.
+ auto log_callback = OsLogger::GetLogFunction();
+ if (log_callback)
+ {
+ DNBLogSetLogCallback(log_callback, nullptr);
+ DNBLog("debugserver will use os_log for internal logging.");
+ }
+ else
+ {
+ // Fall back to ASL support.
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ DNBLog("debugserver will use ASL for internal logging.");
+ }
DNBLogSetLogMask (log_flags);
}
OpenPOWER on IntegriCloud