180814287SRaphael Isemann //===-- StructuredDataDarwinLog.cpp ---------------------------------------===//
275930019STodd Fiala //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
675930019STodd Fiala //
775930019STodd Fiala //===----------------------------------------------------------------------===//
875930019STodd Fiala 
975930019STodd Fiala #include "StructuredDataDarwinLog.h"
1075930019STodd Fiala 
1176e47d48SRaphael Isemann #include <cstring>
1275930019STodd Fiala 
13796ac80bSJonas Devlieghere #include <memory>
1475930019STodd Fiala #include <sstream>
1575930019STodd Fiala 
1675930019STodd Fiala #include "lldb/Breakpoint/StoppointCallbackContext.h"
1775930019STodd Fiala #include "lldb/Core/Debugger.h"
1875930019STodd Fiala #include "lldb/Core/Module.h"
1975930019STodd Fiala #include "lldb/Core/PluginManager.h"
203eb2b44dSZachary Turner #include "lldb/Host/OptionParser.h"
2175930019STodd Fiala #include "lldb/Interpreter/CommandInterpreter.h"
2275930019STodd Fiala #include "lldb/Interpreter/CommandObjectMultiword.h"
2375930019STodd Fiala #include "lldb/Interpreter/CommandReturnObject.h"
2447cbf4a0SPavel Labath #include "lldb/Interpreter/OptionArgParser.h"
2575930019STodd Fiala #include "lldb/Interpreter/OptionValueProperties.h"
2675930019STodd Fiala #include "lldb/Interpreter/OptionValueString.h"
2775930019STodd Fiala #include "lldb/Interpreter/Property.h"
2875930019STodd Fiala #include "lldb/Target/Process.h"
2975930019STodd Fiala #include "lldb/Target/Target.h"
3075930019STodd Fiala #include "lldb/Target/ThreadPlanCallOnFunctionExit.h"
31c34698a8SPavel Labath #include "lldb/Utility/LLDBLog.h"
326f9e6901SZachary Turner #include "lldb/Utility/Log.h"
33bf9a7730SZachary Turner #include "lldb/Utility/RegularExpression.h"
3475930019STodd Fiala 
3575930019STodd Fiala #define DARWIN_LOG_TYPE_VALUE "DarwinLog"
3675930019STodd Fiala 
3775930019STodd Fiala using namespace lldb;
3875930019STodd Fiala using namespace lldb_private;
3975930019STodd Fiala 
40bba9ba8dSJonas Devlieghere LLDB_PLUGIN_DEFINE(StructuredDataDarwinLog)
41fbb4d1e4SJonas Devlieghere 
4275930019STodd Fiala #pragma mark -
4375930019STodd Fiala #pragma mark Anonymous Namespace
4475930019STodd Fiala 
4575930019STodd Fiala // Anonymous namespace
4675930019STodd Fiala 
47b9c1b51eSKate Stone namespace sddarwinlog_private {
4875930019STodd Fiala const uint64_t NANOS_PER_MICRO = 1000;
4975930019STodd Fiala const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000;
5075930019STodd Fiala const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
5175930019STodd Fiala const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60;
5275930019STodd Fiala const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60;
5375930019STodd Fiala 
5475930019STodd Fiala static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true;
5575930019STodd Fiala 
5675930019STodd Fiala /// Global, sticky enable switch.  If true, the user has explicitly
5775930019STodd Fiala /// run the enable command.  When a process launches or is attached to,
5875930019STodd Fiala /// we will enable DarwinLog if either the settings for auto-enable is
5975930019STodd Fiala /// on, or if the user had explicitly run enable at some point prior
6075930019STodd Fiala /// to the launch/attach.
6175930019STodd Fiala static bool s_is_explicitly_enabled;
6275930019STodd Fiala 
6375930019STodd Fiala class EnableOptions;
6475930019STodd Fiala using EnableOptionsSP = std::shared_ptr<EnableOptions>;
6575930019STodd Fiala 
66b9c1b51eSKate Stone using OptionsMap =
67b9c1b51eSKate Stone     std::map<DebuggerWP, EnableOptionsSP, std::owner_less<DebuggerWP>>;
6875930019STodd Fiala 
GetGlobalOptionsMap()69b9c1b51eSKate Stone static OptionsMap &GetGlobalOptionsMap() {
7075930019STodd Fiala   static OptionsMap s_options_map;
7175930019STodd Fiala   return s_options_map;
7275930019STodd Fiala }
7375930019STodd Fiala 
GetGlobalOptionsMapLock()74b9c1b51eSKate Stone static std::mutex &GetGlobalOptionsMapLock() {
7575930019STodd Fiala   static std::mutex s_options_map_lock;
7675930019STodd Fiala   return s_options_map_lock;
7775930019STodd Fiala }
7875930019STodd Fiala 
GetGlobalEnableOptions(const DebuggerSP & debugger_sp)79b9c1b51eSKate Stone EnableOptionsSP GetGlobalEnableOptions(const DebuggerSP &debugger_sp) {
8075930019STodd Fiala   if (!debugger_sp)
8175930019STodd Fiala     return EnableOptionsSP();
8275930019STodd Fiala 
8375930019STodd Fiala   std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
8475930019STodd Fiala   OptionsMap &options_map = GetGlobalOptionsMap();
8575930019STodd Fiala   DebuggerWP debugger_wp(debugger_sp);
8675930019STodd Fiala   auto find_it = options_map.find(debugger_wp);
8775930019STodd Fiala   if (find_it != options_map.end())
8875930019STodd Fiala     return find_it->second;
8975930019STodd Fiala   else
9075930019STodd Fiala     return EnableOptionsSP();
9175930019STodd Fiala }
9275930019STodd Fiala 
SetGlobalEnableOptions(const DebuggerSP & debugger_sp,const EnableOptionsSP & options_sp)93b9c1b51eSKate Stone void SetGlobalEnableOptions(const DebuggerSP &debugger_sp,
94b9c1b51eSKate Stone                             const EnableOptionsSP &options_sp) {
9575930019STodd Fiala   std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
9675930019STodd Fiala   OptionsMap &options_map = GetGlobalOptionsMap();
9775930019STodd Fiala   DebuggerWP debugger_wp(debugger_sp);
9875930019STodd Fiala   auto find_it = options_map.find(debugger_wp);
9975930019STodd Fiala   if (find_it != options_map.end())
10075930019STodd Fiala     find_it->second = options_sp;
10175930019STodd Fiala   else
10275930019STodd Fiala     options_map.insert(std::make_pair(debugger_wp, options_sp));
10375930019STodd Fiala }
10475930019STodd Fiala 
10575930019STodd Fiala #pragma mark -
10675930019STodd Fiala #pragma mark Settings Handling
10775930019STodd Fiala 
10875930019STodd Fiala /// Code to handle the StructuredDataDarwinLog settings
10975930019STodd Fiala 
110971f9ca6SJonas Devlieghere #define LLDB_PROPERTIES_darwinlog
1116a253d37SJordan Rupprecht #include "StructuredDataDarwinLogProperties.inc"
11275930019STodd Fiala 
113971f9ca6SJonas Devlieghere enum {
114971f9ca6SJonas Devlieghere #define LLDB_PROPERTIES_darwinlog
1156a253d37SJordan Rupprecht #include "StructuredDataDarwinLogPropertiesEnum.inc"
116971f9ca6SJonas Devlieghere };
11775930019STodd Fiala 
118b9c1b51eSKate Stone class StructuredDataDarwinLogProperties : public Properties {
11975930019STodd Fiala public:
GetSettingName()120b9c1b51eSKate Stone   static ConstString &GetSettingName() {
12175930019STodd Fiala     static ConstString g_setting_name("darwin-log");
12275930019STodd Fiala     return g_setting_name;
12375930019STodd Fiala   }
12475930019STodd Fiala 
StructuredDataDarwinLogProperties()125b9c1b51eSKate Stone   StructuredDataDarwinLogProperties() : Properties() {
126796ac80bSJonas Devlieghere     m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
127a8ea5955SJonas Devlieghere     m_collection_sp->Initialize(g_darwinlog_properties);
12875930019STodd Fiala   }
12975930019STodd Fiala 
130fd2433e1SJonas Devlieghere   ~StructuredDataDarwinLogProperties() override = default;
13175930019STodd Fiala 
GetEnableOnStartup() const132b9c1b51eSKate Stone   bool GetEnableOnStartup() const {
13375930019STodd Fiala     const uint32_t idx = ePropertyEnableOnStartup;
134b9c1b51eSKate Stone     return m_collection_sp->GetPropertyAtIndexAsBoolean(
135a8ea5955SJonas Devlieghere         nullptr, idx, g_darwinlog_properties[idx].default_uint_value != 0);
13675930019STodd Fiala   }
13775930019STodd Fiala 
GetAutoEnableOptions() const13831d97a5cSZachary Turner   llvm::StringRef GetAutoEnableOptions() const {
13975930019STodd Fiala     const uint32_t idx = ePropertyAutoEnableOptions;
140b9c1b51eSKate Stone     return m_collection_sp->GetPropertyAtIndexAsString(
141a8ea5955SJonas Devlieghere         nullptr, idx, g_darwinlog_properties[idx].default_cstr_value);
14275930019STodd Fiala   }
14375930019STodd Fiala 
GetLoggingModuleName() const144b9c1b51eSKate Stone   const char *GetLoggingModuleName() const { return "libsystem_trace.dylib"; }
14575930019STodd Fiala };
14675930019STodd Fiala 
GetGlobalProperties()1473d7161e3SPavel Labath static StructuredDataDarwinLogProperties &GetGlobalProperties() {
1483d7161e3SPavel Labath   static StructuredDataDarwinLogProperties g_settings;
1493d7161e3SPavel Labath   return g_settings;
15075930019STodd Fiala }
15175930019STodd Fiala 
152b9c1b51eSKate Stone const char *const s_filter_attributes[] = {
15375930019STodd Fiala     "activity",       // current activity
15475930019STodd Fiala     "activity-chain", // entire activity chain, each level separated by ':'
15575930019STodd Fiala     "category",       // category of the log message
15675930019STodd Fiala     "message",        // message contents, fully expanded
15775930019STodd Fiala     "subsystem"       // subsystem of the log message
15875930019STodd Fiala 
1594ebdee0aSBruce Mitchener     // Consider implementing this action as it would be cheaper to filter.
16005097246SAdrian Prantl     // "message" requires always formatting the message, which is a waste of
16105097246SAdrian Prantl     // cycles if it ends up being rejected. "format",      // format string
16205097246SAdrian Prantl     // used to format message text
16375930019STodd Fiala };
16475930019STodd Fiala 
GetDarwinLogTypeName()1650e4c4821SAdrian Prantl static ConstString GetDarwinLogTypeName() {
16675930019STodd Fiala   static const ConstString s_key_name("DarwinLog");
16775930019STodd Fiala   return s_key_name;
16875930019STodd Fiala }
16975930019STodd Fiala 
GetLogEventType()1700e4c4821SAdrian Prantl static ConstString GetLogEventType() {
17175930019STodd Fiala   static const ConstString s_event_type("log");
17275930019STodd Fiala   return s_event_type;
17375930019STodd Fiala }
17475930019STodd Fiala 
17575930019STodd Fiala class FilterRule;
17675930019STodd Fiala using FilterRuleSP = std::shared_ptr<FilterRule>;
17775930019STodd Fiala 
178b9c1b51eSKate Stone class FilterRule {
17975930019STodd Fiala public:
180fd2433e1SJonas Devlieghere   virtual ~FilterRule() = default;
18175930019STodd Fiala 
18275930019STodd Fiala   using OperationCreationFunc =
183b9c1b51eSKate Stone       std::function<FilterRuleSP(bool accept, size_t attribute_index,
18497206d57SZachary Turner                                  const std::string &op_arg, Status &error)>;
18575930019STodd Fiala 
RegisterOperation(ConstString operation,const OperationCreationFunc & creation_func)1860e4c4821SAdrian Prantl   static void RegisterOperation(ConstString operation,
187b9c1b51eSKate Stone                                 const OperationCreationFunc &creation_func) {
188b9c1b51eSKate Stone     GetCreationFuncMap().insert(std::make_pair(operation, creation_func));
18975930019STodd Fiala   }
19075930019STodd Fiala 
CreateRule(bool match_accepts,size_t attribute,ConstString operation,const std::string & op_arg,Status & error)191b9c1b51eSKate Stone   static FilterRuleSP CreateRule(bool match_accepts, size_t attribute,
1920e4c4821SAdrian Prantl                                  ConstString operation,
19397206d57SZachary Turner                                  const std::string &op_arg, Status &error) {
19475930019STodd Fiala     // Find the creation func for this type of filter rule.
19575930019STodd Fiala     auto map = GetCreationFuncMap();
19675930019STodd Fiala     auto find_it = map.find(operation);
197b9c1b51eSKate Stone     if (find_it == map.end()) {
19875930019STodd Fiala       error.SetErrorStringWithFormat("unknown filter operation \""
19975930019STodd Fiala                                      "%s\"",
20075930019STodd Fiala                                      operation.GetCString());
20175930019STodd Fiala       return FilterRuleSP();
20275930019STodd Fiala     }
20375930019STodd Fiala 
20475930019STodd Fiala     return find_it->second(match_accepts, attribute, op_arg, error);
20575930019STodd Fiala   }
20675930019STodd Fiala 
Serialize() const207b9c1b51eSKate Stone   StructuredData::ObjectSP Serialize() const {
208b9c1b51eSKate Stone     StructuredData::Dictionary *dict_p = new StructuredData::Dictionary();
20975930019STodd Fiala 
21075930019STodd Fiala     // Indicate whether this is an accept or reject rule.
21175930019STodd Fiala     dict_p->AddBooleanItem("accept", m_accept);
21275930019STodd Fiala 
21305097246SAdrian Prantl     // Indicate which attribute of the message this filter references. This can
21405097246SAdrian Prantl     // drop into the rule-specific DoSerialization if we get to the point where
21505097246SAdrian Prantl     // not all FilterRule derived classes work on an attribute.  (e.g. logical
21605097246SAdrian Prantl     // and/or and other compound operations).
217b9c1b51eSKate Stone     dict_p->AddStringItem("attribute", s_filter_attributes[m_attribute_index]);
21875930019STodd Fiala 
21975930019STodd Fiala     // Indicate the type of the rule.
22075930019STodd Fiala     dict_p->AddStringItem("type", GetOperationType().GetCString());
22175930019STodd Fiala 
22275930019STodd Fiala     // Let the rule add its own specific details here.
22375930019STodd Fiala     DoSerialization(*dict_p);
22475930019STodd Fiala 
22575930019STodd Fiala     return StructuredData::ObjectSP(dict_p);
22675930019STodd Fiala   }
22775930019STodd Fiala 
228b9c1b51eSKate Stone   virtual void Dump(Stream &stream) const = 0;
22975930019STodd Fiala 
GetOperationType() const2300e4c4821SAdrian Prantl   ConstString GetOperationType() const { return m_operation; }
23175930019STodd Fiala 
23275930019STodd Fiala protected:
FilterRule(bool accept,size_t attribute_index,ConstString operation)2330e4c4821SAdrian Prantl   FilterRule(bool accept, size_t attribute_index, ConstString operation)
234b9c1b51eSKate Stone       : m_accept(accept), m_attribute_index(attribute_index),
235b9c1b51eSKate Stone         m_operation(operation) {}
23675930019STodd Fiala 
237b9c1b51eSKate Stone   virtual void DoSerialization(StructuredData::Dictionary &dict) const = 0;
23875930019STodd Fiala 
GetMatchAccepts() const239b9c1b51eSKate Stone   bool GetMatchAccepts() const { return m_accept; }
24075930019STodd Fiala 
GetFilterAttribute() const241b9c1b51eSKate Stone   const char *GetFilterAttribute() const {
24275930019STodd Fiala     return s_filter_attributes[m_attribute_index];
24375930019STodd Fiala   }
24475930019STodd Fiala 
24575930019STodd Fiala private:
24675930019STodd Fiala   using CreationFuncMap = std::map<ConstString, OperationCreationFunc>;
24775930019STodd Fiala 
GetCreationFuncMap()248b9c1b51eSKate Stone   static CreationFuncMap &GetCreationFuncMap() {
24975930019STodd Fiala     static CreationFuncMap s_map;
25075930019STodd Fiala     return s_map;
25175930019STodd Fiala   }
25275930019STodd Fiala 
25375930019STodd Fiala   const bool m_accept;
25475930019STodd Fiala   const size_t m_attribute_index;
25575930019STodd Fiala   const ConstString m_operation;
25675930019STodd Fiala };
25775930019STodd Fiala 
25875930019STodd Fiala using FilterRules = std::vector<FilterRuleSP>;
25975930019STodd Fiala 
260b9c1b51eSKate Stone class RegexFilterRule : public FilterRule {
26175930019STodd Fiala public:
RegisterOperation()262b9c1b51eSKate Stone   static void RegisterOperation() {
263b9c1b51eSKate Stone     FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
26475930019STodd Fiala   }
26575930019STodd Fiala 
Dump(Stream & stream) const266b9c1b51eSKate Stone   void Dump(Stream &stream) const override {
267b9c1b51eSKate Stone     stream.Printf("%s %s regex %s", GetMatchAccepts() ? "accept" : "reject",
26875930019STodd Fiala                   GetFilterAttribute(), m_regex_text.c_str());
26975930019STodd Fiala   }
27075930019STodd Fiala 
27175930019STodd Fiala protected:
DoSerialization(StructuredData::Dictionary & dict) const272b9c1b51eSKate Stone   void DoSerialization(StructuredData::Dictionary &dict) const override {
27375930019STodd Fiala     dict.AddStringItem("regex", m_regex_text);
27475930019STodd Fiala   }
27575930019STodd Fiala 
27675930019STodd Fiala private:
CreateOperation(bool accept,size_t attribute_index,const std::string & op_arg,Status & error)277b9c1b51eSKate Stone   static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
27897206d57SZachary Turner                                       const std::string &op_arg,
27997206d57SZachary Turner                                       Status &error) {
28075930019STodd Fiala     // We treat the op_arg as a regex.  Validate it.
281b9c1b51eSKate Stone     if (op_arg.empty()) {
28275930019STodd Fiala       error.SetErrorString("regex filter type requires a regex "
28375930019STodd Fiala                            "argument");
28475930019STodd Fiala       return FilterRuleSP();
28575930019STodd Fiala     }
28675930019STodd Fiala 
28775930019STodd Fiala     // Instantiate the regex so we can report any errors.
28895eae423SZachary Turner     auto regex = RegularExpression(op_arg);
2893af3f1e8SJonas Devlieghere     if (llvm::Error err = regex.GetError()) {
2903af3f1e8SJonas Devlieghere       error.SetErrorString(llvm::toString(std::move(err)));
29175930019STodd Fiala       return FilterRuleSP();
29275930019STodd Fiala     }
29375930019STodd Fiala 
29475930019STodd Fiala     // We passed all our checks, this appears fine.
29575930019STodd Fiala     error.Clear();
296b9c1b51eSKate Stone     return FilterRuleSP(new RegexFilterRule(accept, attribute_index, op_arg));
29775930019STodd Fiala   }
29875930019STodd Fiala 
StaticGetOperation()2990e4c4821SAdrian Prantl   static ConstString StaticGetOperation() {
30075930019STodd Fiala     static ConstString s_operation("regex");
30175930019STodd Fiala     return s_operation;
30275930019STodd Fiala   }
30375930019STodd Fiala 
RegexFilterRule(bool accept,size_t attribute_index,const std::string & regex_text)30475930019STodd Fiala   RegexFilterRule(bool accept, size_t attribute_index,
305b9c1b51eSKate Stone                   const std::string &regex_text)
306b9c1b51eSKate Stone       : FilterRule(accept, attribute_index, StaticGetOperation()),
307b9c1b51eSKate Stone         m_regex_text(regex_text) {}
30875930019STodd Fiala 
30975930019STodd Fiala   const std::string m_regex_text;
31075930019STodd Fiala };
31175930019STodd Fiala 
312b9c1b51eSKate Stone class ExactMatchFilterRule : public FilterRule {
31375930019STodd Fiala public:
RegisterOperation()314b9c1b51eSKate Stone   static void RegisterOperation() {
315b9c1b51eSKate Stone     FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
31675930019STodd Fiala   }
31775930019STodd Fiala 
Dump(Stream & stream) const318b9c1b51eSKate Stone   void Dump(Stream &stream) const override {
319b9c1b51eSKate Stone     stream.Printf("%s %s match %s", GetMatchAccepts() ? "accept" : "reject",
32075930019STodd Fiala                   GetFilterAttribute(), m_match_text.c_str());
32175930019STodd Fiala   }
32275930019STodd Fiala 
32375930019STodd Fiala protected:
DoSerialization(StructuredData::Dictionary & dict) const324b9c1b51eSKate Stone   void DoSerialization(StructuredData::Dictionary &dict) const override {
32575930019STodd Fiala     dict.AddStringItem("exact_text", m_match_text);
32675930019STodd Fiala   }
32775930019STodd Fiala 
32875930019STodd Fiala private:
CreateOperation(bool accept,size_t attribute_index,const std::string & op_arg,Status & error)329b9c1b51eSKate Stone   static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
33097206d57SZachary Turner                                       const std::string &op_arg,
33197206d57SZachary Turner                                       Status &error) {
332b9c1b51eSKate Stone     if (op_arg.empty()) {
33375930019STodd Fiala       error.SetErrorString("exact match filter type requires an "
33475930019STodd Fiala                            "argument containing the text that must "
33575930019STodd Fiala                            "match the specified message attribute.");
33675930019STodd Fiala       return FilterRuleSP();
33775930019STodd Fiala     }
33875930019STodd Fiala 
33975930019STodd Fiala     error.Clear();
340b9c1b51eSKate Stone     return FilterRuleSP(
341b9c1b51eSKate Stone         new ExactMatchFilterRule(accept, attribute_index, op_arg));
34275930019STodd Fiala   }
34375930019STodd Fiala 
StaticGetOperation()3440e4c4821SAdrian Prantl   static ConstString StaticGetOperation() {
34575930019STodd Fiala     static ConstString s_operation("match");
34675930019STodd Fiala     return s_operation;
34775930019STodd Fiala   }
34875930019STodd Fiala 
ExactMatchFilterRule(bool accept,size_t attribute_index,const std::string & match_text)34975930019STodd Fiala   ExactMatchFilterRule(bool accept, size_t attribute_index,
350b9c1b51eSKate Stone                        const std::string &match_text)
351b9c1b51eSKate Stone       : FilterRule(accept, attribute_index, StaticGetOperation()),
352b9c1b51eSKate Stone         m_match_text(match_text) {}
35375930019STodd Fiala 
35475930019STodd Fiala   const std::string m_match_text;
35575930019STodd Fiala };
35675930019STodd Fiala 
RegisterFilterOperations()357b9c1b51eSKate Stone static void RegisterFilterOperations() {
35875930019STodd Fiala   ExactMatchFilterRule::RegisterOperation();
35975930019STodd Fiala   RegexFilterRule::RegisterOperation();
36075930019STodd Fiala }
36175930019STodd Fiala 
36275930019STodd Fiala // =========================================================================
36375930019STodd Fiala // Commands
36475930019STodd Fiala // =========================================================================
36575930019STodd Fiala 
36675930019STodd Fiala /// Provides the main on-off switch for enabling darwin logging.
36775930019STodd Fiala ///
36875930019STodd Fiala /// It is valid to run the enable command when logging is already enabled.
36975930019STodd Fiala /// This resets the logging with whatever settings are currently set.
3701f0f5b5bSZachary Turner 
3718fe53c49STatyana Krasnukha static constexpr OptionDefinition g_enable_option_table[] = {
37205097246SAdrian Prantl     // Source stream include/exclude options (the first-level filter). This one
37305097246SAdrian Prantl     // should be made as small as possible as everything that goes through here
37405097246SAdrian Prantl     // must be processed by the process monitor.
3751f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "any-process", 'a', OptionParser::eNoArgument,
3768fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgTypeNone,
3771f0f5b5bSZachary Turner      "Specifies log messages from other related processes should be "
3781f0f5b5bSZachary Turner      "included."},
3791f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument, nullptr,
3808fe53c49STatyana Krasnukha      {}, 0, eArgTypeNone,
3811f0f5b5bSZachary Turner      "Specifies debug-level log messages should be included.  Specifying"
3821f0f5b5bSZachary Turner      " --debug implies --info."},
3831f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument, nullptr,
3848fe53c49STatyana Krasnukha      {}, 0, eArgTypeNone,
3851f0f5b5bSZachary Turner      "Specifies info-level log messages should be included."},
3861f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "filter", 'f', OptionParser::eRequiredArgument,
3878fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgRawInput,
38805097246SAdrian Prantl      // There doesn't appear to be a great way for me to have these multi-line,
38905097246SAdrian Prantl      // formatted tables in help.  This looks mostly right but there are extra
39005097246SAdrian Prantl      // linefeeds added at seemingly random spots, and indentation isn't
39105097246SAdrian Prantl      // handled properly on those lines.
3921f0f5b5bSZachary Turner      "Appends a filter rule to the log message filter chain.  Multiple "
3931f0f5b5bSZachary Turner      "rules may be added by specifying this option multiple times, "
3941f0f5b5bSZachary Turner      "once per filter rule.  Filter rules are processed in the order "
3951f0f5b5bSZachary Turner      "they are specified, with the --no-match-accepts setting used "
3961f0f5b5bSZachary Turner      "for any message that doesn't match one of the rules.\n"
3971f0f5b5bSZachary Turner      "\n"
3981f0f5b5bSZachary Turner      "    Filter spec format:\n"
3991f0f5b5bSZachary Turner      "\n"
4001f0f5b5bSZachary Turner      "    --filter \"{action} {attribute} {op}\"\n"
4011f0f5b5bSZachary Turner      "\n"
4021f0f5b5bSZachary Turner      "    {action} :=\n"
4031f0f5b5bSZachary Turner      "      accept |\n"
4041f0f5b5bSZachary Turner      "      reject\n"
4051f0f5b5bSZachary Turner      "\n"
4061f0f5b5bSZachary Turner      "    {attribute} :=\n"
4071f0f5b5bSZachary Turner      "       activity       |  // message's most-derived activity\n"
4081f0f5b5bSZachary Turner      "       activity-chain |  // message's {parent}:{child} activity\n"
4091f0f5b5bSZachary Turner      "       category       |  // message's category\n"
4101f0f5b5bSZachary Turner      "       message        |  // message's expanded contents\n"
4111f0f5b5bSZachary Turner      "       subsystem      |  // message's subsystem\n"
4121f0f5b5bSZachary Turner      "\n"
4131f0f5b5bSZachary Turner      "    {op} :=\n"
4141f0f5b5bSZachary Turner      "      match {exact-match-text} |\n"
4151f0f5b5bSZachary Turner      "      regex {search-regex}\n"
4161f0f5b5bSZachary Turner      "\n"
4171f0f5b5bSZachary Turner      "The regex flavor used is the C++ std::regex ECMAScript format.  "
4181f0f5b5bSZachary Turner      "Prefer character classes like [[:digit:]] to \\d and the like, as "
4191f0f5b5bSZachary Turner      "getting the backslashes escaped through properly is error-prone."},
4201f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "live-stream", 'l',
4218fe53c49STatyana Krasnukha      OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
4221f0f5b5bSZachary Turner      "Specify whether logging events are live-streamed or buffered.  "
4231f0f5b5bSZachary Turner      "True indicates live streaming, false indicates buffered.  The "
4241f0f5b5bSZachary Turner      "default is true (live streaming).  Live streaming will deliver "
4251f0f5b5bSZachary Turner      "log messages with less delay, but buffered capture mode has less "
4261f0f5b5bSZachary Turner      "of an observer effect."},
4271f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n',
4288fe53c49STatyana Krasnukha      OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
4291f0f5b5bSZachary Turner      "Specify whether a log message that doesn't match any filter rule "
4301f0f5b5bSZachary Turner      "is accepted or rejected, where true indicates accept.  The "
4311f0f5b5bSZachary Turner      "default is true."},
4321f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e',
4338fe53c49STatyana Krasnukha      OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
4341f0f5b5bSZachary Turner      "Specify whether os_log()/NSLog() messages are echoed to the "
4351f0f5b5bSZachary Turner      "target program's stderr.  When DarwinLog is enabled, we shut off "
4361f0f5b5bSZachary Turner      "the mirroring of os_log()/NSLog() to the program's stderr.  "
4371f0f5b5bSZachary Turner      "Setting this flag to true will restore the stderr mirroring."
4381f0f5b5bSZachary Turner      "The default is false."},
4391f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "broadcast-events", 'b',
4408fe53c49STatyana Krasnukha      OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
4411f0f5b5bSZachary Turner      "Specify if the plugin should broadcast events.  Broadcasting "
4421f0f5b5bSZachary Turner      "log events is a requirement for displaying the log entries in "
4431f0f5b5bSZachary Turner      "LLDB command-line.  It is also required if LLDB clients want to "
4441f0f5b5bSZachary Turner      "process log events.  The default is true."},
4451f0f5b5bSZachary Turner     // Message formatting options
4461f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r',
4478fe53c49STatyana Krasnukha      OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
4481f0f5b5bSZachary Turner      "Include timestamp in the message header when printing a log "
4491f0f5b5bSZachary Turner      "message.  The timestamp is relative to the first displayed "
4501f0f5b5bSZachary Turner      "message."},
4511f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "subsystem", 's', OptionParser::eNoArgument,
4528fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgTypeNone,
453173946dcSBruce Mitchener      "Include the subsystem in the message header when displaying "
4541f0f5b5bSZachary Turner      "a log message."},
4551f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "category", 'c', OptionParser::eNoArgument,
4568fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgTypeNone,
457173946dcSBruce Mitchener      "Include the category in the message header when displaying "
4581f0f5b5bSZachary Turner      "a log message."},
4591f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "activity-chain", 'C', OptionParser::eNoArgument,
4608fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgTypeNone,
4614ebdee0aSBruce Mitchener      "Include the activity parent-child chain in the message header "
4621f0f5b5bSZachary Turner      "when displaying a log message.  The activity hierarchy is "
4631f0f5b5bSZachary Turner      "displayed as {grandparent-activity}:"
4641f0f5b5bSZachary Turner      "{parent-activity}:{activity}[:...]."},
4651f0f5b5bSZachary Turner     {LLDB_OPT_SET_ALL, false, "all-fields", 'A', OptionParser::eNoArgument,
4668fe53c49STatyana Krasnukha      nullptr, {}, 0, eArgTypeNone,
4671f0f5b5bSZachary Turner      "Shortcut to specify that all header fields should be displayed."}};
4681f0f5b5bSZachary Turner 
469b9c1b51eSKate Stone class EnableOptions : public Options {
47075930019STodd Fiala public:
EnableOptions()471b9c1b51eSKate Stone   EnableOptions()
4729494c510SJonas Devlieghere       : Options(),
47375930019STodd Fiala         m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS),
4749494c510SJonas Devlieghere         m_filter_rules() {}
47575930019STodd Fiala 
OptionParsingStarting(ExecutionContext * execution_context)476b9c1b51eSKate Stone   void OptionParsingStarting(ExecutionContext *execution_context) override {
47775930019STodd Fiala     m_include_debug_level = false;
47875930019STodd Fiala     m_include_info_level = false;
47975930019STodd Fiala     m_include_any_process = false;
48075930019STodd Fiala     m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS;
48175930019STodd Fiala     m_echo_to_stderr = false;
48275930019STodd Fiala     m_display_timestamp_relative = false;
48375930019STodd Fiala     m_display_subsystem = false;
48475930019STodd Fiala     m_display_category = false;
48575930019STodd Fiala     m_display_activity_chain = false;
48675930019STodd Fiala     m_broadcast_events = true;
48775930019STodd Fiala     m_live_stream = true;
48875930019STodd Fiala     m_filter_rules.clear();
48975930019STodd Fiala   }
49075930019STodd Fiala 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)49197206d57SZachary Turner   Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
492b9c1b51eSKate Stone                         ExecutionContext *execution_context) override {
49397206d57SZachary Turner     Status error;
49475930019STodd Fiala 
49575930019STodd Fiala     const int short_option = m_getopt_table[option_idx].val;
496b9c1b51eSKate Stone     switch (short_option) {
49775930019STodd Fiala     case 'a':
49875930019STodd Fiala       m_include_any_process = true;
49975930019STodd Fiala       break;
50075930019STodd Fiala 
50175930019STodd Fiala     case 'A':
50275930019STodd Fiala       m_display_timestamp_relative = true;
50375930019STodd Fiala       m_display_category = true;
50475930019STodd Fiala       m_display_subsystem = true;
50575930019STodd Fiala       m_display_activity_chain = true;
50675930019STodd Fiala       break;
50775930019STodd Fiala 
50875930019STodd Fiala     case 'b':
50947cbf4a0SPavel Labath       m_broadcast_events =
51047cbf4a0SPavel Labath           OptionArgParser::ToBoolean(option_arg, true, nullptr);
51175930019STodd Fiala       break;
51275930019STodd Fiala 
51375930019STodd Fiala     case 'c':
51475930019STodd Fiala       m_display_category = true;
51575930019STodd Fiala       break;
51675930019STodd Fiala 
51775930019STodd Fiala     case 'C':
51875930019STodd Fiala       m_display_activity_chain = true;
51975930019STodd Fiala       break;
52075930019STodd Fiala 
52175930019STodd Fiala     case 'd':
52275930019STodd Fiala       m_include_debug_level = true;
52375930019STodd Fiala       break;
52475930019STodd Fiala 
52575930019STodd Fiala     case 'e':
52647cbf4a0SPavel Labath       m_echo_to_stderr = OptionArgParser::ToBoolean(option_arg, false, nullptr);
52775930019STodd Fiala       break;
52875930019STodd Fiala 
52975930019STodd Fiala     case 'f':
530fe11483bSZachary Turner       return ParseFilterRule(option_arg);
53175930019STodd Fiala 
53275930019STodd Fiala     case 'i':
53375930019STodd Fiala       m_include_info_level = true;
53475930019STodd Fiala       break;
53575930019STodd Fiala 
53675930019STodd Fiala     case 'l':
53747cbf4a0SPavel Labath       m_live_stream = OptionArgParser::ToBoolean(option_arg, false, nullptr);
53875930019STodd Fiala       break;
53975930019STodd Fiala 
54075930019STodd Fiala     case 'n':
54175930019STodd Fiala       m_filter_fall_through_accepts =
54247cbf4a0SPavel Labath           OptionArgParser::ToBoolean(option_arg, true, nullptr);
54375930019STodd Fiala       break;
54475930019STodd Fiala 
54575930019STodd Fiala     case 'r':
54675930019STodd Fiala       m_display_timestamp_relative = true;
54775930019STodd Fiala       break;
54875930019STodd Fiala 
54975930019STodd Fiala     case 's':
55075930019STodd Fiala       m_display_subsystem = true;
55175930019STodd Fiala       break;
55275930019STodd Fiala 
55375930019STodd Fiala     default:
554b9c1b51eSKate Stone       error.SetErrorStringWithFormat("unsupported option '%c'", short_option);
55575930019STodd Fiala     }
55675930019STodd Fiala     return error;
55775930019STodd Fiala   }
55875930019STodd Fiala 
GetDefinitions()5591f0f5b5bSZachary Turner   llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
56070602439SZachary Turner     return llvm::makeArrayRef(g_enable_option_table);
56175930019STodd Fiala   }
56275930019STodd Fiala 
BuildConfigurationData(bool enabled)563b9c1b51eSKate Stone   StructuredData::DictionarySP BuildConfigurationData(bool enabled) {
564b9c1b51eSKate Stone     StructuredData::DictionarySP config_sp(new StructuredData::Dictionary());
56575930019STodd Fiala 
56675930019STodd Fiala     // Set the basic enabled state.
56775930019STodd Fiala     config_sp->AddBooleanItem("enabled", enabled);
56875930019STodd Fiala 
56975930019STodd Fiala     // If we're disabled, there's nothing more to add.
57075930019STodd Fiala     if (!enabled)
57175930019STodd Fiala       return config_sp;
57275930019STodd Fiala 
57375930019STodd Fiala     // Handle source stream flags.
57475930019STodd Fiala     auto source_flags_sp =
57575930019STodd Fiala         StructuredData::DictionarySP(new StructuredData::Dictionary());
57675930019STodd Fiala     config_sp->AddItem("source-flags", source_flags_sp);
57775930019STodd Fiala 
578b9c1b51eSKate Stone     source_flags_sp->AddBooleanItem("any-process", m_include_any_process);
579b9c1b51eSKate Stone     source_flags_sp->AddBooleanItem("debug-level", m_include_debug_level);
5804ebdee0aSBruce Mitchener     // The debug-level flag, if set, implies info-level.
581b9c1b51eSKate Stone     source_flags_sp->AddBooleanItem("info-level", m_include_info_level ||
58275930019STodd Fiala                                                       m_include_debug_level);
583b9c1b51eSKate Stone     source_flags_sp->AddBooleanItem("live-stream", m_live_stream);
58475930019STodd Fiala 
58575930019STodd Fiala     // Specify default filter rule (the fall-through)
58675930019STodd Fiala     config_sp->AddBooleanItem("filter-fall-through-accepts",
58775930019STodd Fiala                               m_filter_fall_through_accepts);
58875930019STodd Fiala 
58975930019STodd Fiala     // Handle filter rules
590b9c1b51eSKate Stone     if (!m_filter_rules.empty()) {
59175930019STodd Fiala       auto json_filter_rules_sp =
59275930019STodd Fiala           StructuredData::ArraySP(new StructuredData::Array);
593b9c1b51eSKate Stone       config_sp->AddItem("filter-rules", json_filter_rules_sp);
594b9c1b51eSKate Stone       for (auto &rule_sp : m_filter_rules) {
59575930019STodd Fiala         if (!rule_sp)
59675930019STodd Fiala           continue;
59775930019STodd Fiala         json_filter_rules_sp->AddItem(rule_sp->Serialize());
59875930019STodd Fiala       }
59975930019STodd Fiala     }
60075930019STodd Fiala     return config_sp;
60175930019STodd Fiala   }
60275930019STodd Fiala 
GetIncludeDebugLevel() const603b9c1b51eSKate Stone   bool GetIncludeDebugLevel() const { return m_include_debug_level; }
60475930019STodd Fiala 
GetIncludeInfoLevel() const605b9c1b51eSKate Stone   bool GetIncludeInfoLevel() const {
60675930019STodd Fiala     // Specifying debug level implies info level.
60775930019STodd Fiala     return m_include_info_level || m_include_debug_level;
60875930019STodd Fiala   }
60975930019STodd Fiala 
GetFilterRules() const610b9c1b51eSKate Stone   const FilterRules &GetFilterRules() const { return m_filter_rules; }
61175930019STodd Fiala 
GetFallthroughAccepts() const612b9c1b51eSKate Stone   bool GetFallthroughAccepts() const { return m_filter_fall_through_accepts; }
61375930019STodd Fiala 
GetEchoToStdErr() const614b9c1b51eSKate Stone   bool GetEchoToStdErr() const { return m_echo_to_stderr; }
61575930019STodd Fiala 
GetDisplayTimestampRelative() const616b9c1b51eSKate Stone   bool GetDisplayTimestampRelative() const {
61775930019STodd Fiala     return m_display_timestamp_relative;
61875930019STodd Fiala   }
61975930019STodd Fiala 
GetDisplaySubsystem() const620b9c1b51eSKate Stone   bool GetDisplaySubsystem() const { return m_display_subsystem; }
GetDisplayCategory() const621b9c1b51eSKate Stone   bool GetDisplayCategory() const { return m_display_category; }
GetDisplayActivityChain() const622b9c1b51eSKate Stone   bool GetDisplayActivityChain() const { return m_display_activity_chain; }
623b9c1b51eSKate Stone 
GetDisplayAnyHeaderFields() const624b9c1b51eSKate Stone   bool GetDisplayAnyHeaderFields() const {
625b9c1b51eSKate Stone     return m_display_timestamp_relative || m_display_activity_chain ||
626b9c1b51eSKate Stone            m_display_subsystem || m_display_category;
62775930019STodd Fiala   }
62875930019STodd Fiala 
GetBroadcastEvents() const629b9c1b51eSKate Stone   bool GetBroadcastEvents() const { return m_broadcast_events; }
63075930019STodd Fiala 
63175930019STodd Fiala private:
ParseFilterRule(llvm::StringRef rule_text)63297206d57SZachary Turner   Status ParseFilterRule(llvm::StringRef rule_text) {
63397206d57SZachary Turner     Status error;
63475930019STodd Fiala 
6356560d2c2SZachary Turner     if (rule_text.empty()) {
63675930019STodd Fiala       error.SetErrorString("invalid rule_text");
63775930019STodd Fiala       return error;
63875930019STodd Fiala     }
63975930019STodd Fiala 
64075930019STodd Fiala     // filter spec format:
64175930019STodd Fiala     //
64275930019STodd Fiala     // {action} {attribute} {op}
64375930019STodd Fiala     //
64475930019STodd Fiala     // {action} :=
64575930019STodd Fiala     //   accept |
64675930019STodd Fiala     //   reject
64775930019STodd Fiala     //
64875930019STodd Fiala     // {attribute} :=
64975930019STodd Fiala     //   category       |
65075930019STodd Fiala     //   subsystem      |
65175930019STodd Fiala     //   activity       |
65275930019STodd Fiala     //   activity-chain |
65375930019STodd Fiala     //   message        |
65475930019STodd Fiala     //   format
65575930019STodd Fiala     //
65675930019STodd Fiala     // {op} :=
65775930019STodd Fiala     //   match {exact-match-text} |
65875930019STodd Fiala     //   regex {search-regex}
65975930019STodd Fiala 
66075930019STodd Fiala     // Parse action.
661c70f3686SFangrui Song     auto action_end_pos = rule_text.find(' ');
662b9c1b51eSKate Stone     if (action_end_pos == std::string::npos) {
66375930019STodd Fiala       error.SetErrorStringWithFormat("could not parse filter rule "
66475930019STodd Fiala                                      "action from \"%s\"",
6656560d2c2SZachary Turner                                      rule_text.str().c_str());
66675930019STodd Fiala       return error;
66775930019STodd Fiala     }
66875930019STodd Fiala     auto action = rule_text.substr(0, action_end_pos);
66975930019STodd Fiala     bool accept;
67075930019STodd Fiala     if (action == "accept")
67175930019STodd Fiala       accept = true;
67275930019STodd Fiala     else if (action == "reject")
67375930019STodd Fiala       accept = false;
674b9c1b51eSKate Stone     else {
6756560d2c2SZachary Turner       error.SetErrorString("filter action must be \"accept\" or \"deny\"");
67675930019STodd Fiala       return error;
67775930019STodd Fiala     }
67875930019STodd Fiala 
67975930019STodd Fiala     // parse attribute
68075930019STodd Fiala     auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1);
681b9c1b51eSKate Stone     if (attribute_end_pos == std::string::npos) {
68275930019STodd Fiala       error.SetErrorStringWithFormat("could not parse filter rule "
68375930019STodd Fiala                                      "attribute from \"%s\"",
6846560d2c2SZachary Turner                                      rule_text.str().c_str());
68575930019STodd Fiala       return error;
68675930019STodd Fiala     }
687b9c1b51eSKate Stone     auto attribute = rule_text.substr(action_end_pos + 1,
68875930019STodd Fiala                                       attribute_end_pos - (action_end_pos + 1));
68975930019STodd Fiala     auto attribute_index = MatchAttributeIndex(attribute);
690b9c1b51eSKate Stone     if (attribute_index < 0) {
69175930019STodd Fiala       error.SetErrorStringWithFormat("filter rule attribute unknown: "
692b9c1b51eSKate Stone                                      "%s",
6936560d2c2SZachary Turner                                      attribute.str().c_str());
69475930019STodd Fiala       return error;
69575930019STodd Fiala     }
69675930019STodd Fiala 
69775930019STodd Fiala     // parse operation
69875930019STodd Fiala     auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1);
699b9c1b51eSKate Stone     auto operation = rule_text.substr(
700b9c1b51eSKate Stone         attribute_end_pos + 1, operation_end_pos - (attribute_end_pos + 1));
70175930019STodd Fiala 
70275930019STodd Fiala     // add filter spec
703adcd0268SBenjamin Kramer     auto rule_sp = FilterRule::CreateRule(
704adcd0268SBenjamin Kramer         accept, attribute_index, ConstString(operation),
705adcd0268SBenjamin Kramer         std::string(rule_text.substr(operation_end_pos + 1)), error);
70675930019STodd Fiala 
70775930019STodd Fiala     if (rule_sp && error.Success())
70875930019STodd Fiala       m_filter_rules.push_back(rule_sp);
70975930019STodd Fiala 
71075930019STodd Fiala     return error;
71175930019STodd Fiala   }
71275930019STodd Fiala 
MatchAttributeIndex(llvm::StringRef attribute_name) const7136560d2c2SZachary Turner   int MatchAttributeIndex(llvm::StringRef attribute_name) const {
7146560d2c2SZachary Turner     for (const auto &Item : llvm::enumerate(s_filter_attributes)) {
715db6310c6SZachary Turner       if (attribute_name == Item.value())
716db6310c6SZachary Turner         return Item.index();
71775930019STodd Fiala     }
71875930019STodd Fiala 
71975930019STodd Fiala     // We didn't match anything.
72075930019STodd Fiala     return -1;
72175930019STodd Fiala   }
72275930019STodd Fiala 
7239494c510SJonas Devlieghere   bool m_include_debug_level = false;
7249494c510SJonas Devlieghere   bool m_include_info_level = false;
7259494c510SJonas Devlieghere   bool m_include_any_process = false;
72675930019STodd Fiala   bool m_filter_fall_through_accepts;
7279494c510SJonas Devlieghere   bool m_echo_to_stderr = false;
7289494c510SJonas Devlieghere   bool m_display_timestamp_relative = false;
7299494c510SJonas Devlieghere   bool m_display_subsystem = false;
7309494c510SJonas Devlieghere   bool m_display_category = false;
7319494c510SJonas Devlieghere   bool m_display_activity_chain = false;
7329494c510SJonas Devlieghere   bool m_broadcast_events = true;
7339494c510SJonas Devlieghere   bool m_live_stream = true;
73475930019STodd Fiala   FilterRules m_filter_rules;
73575930019STodd Fiala };
73675930019STodd Fiala 
737b9c1b51eSKate Stone class EnableCommand : public CommandObjectParsed {
73875930019STodd Fiala public:
EnableCommand(CommandInterpreter & interpreter,bool enable,const char * name,const char * help,const char * syntax)739b9c1b51eSKate Stone   EnableCommand(CommandInterpreter &interpreter, bool enable, const char *name,
740b9c1b51eSKate Stone                 const char *help, const char *syntax)
741b9c1b51eSKate Stone       : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable),
742b9c1b51eSKate Stone         m_options_sp(enable ? new EnableOptions() : nullptr) {}
74375930019STodd Fiala 
74475930019STodd Fiala protected:
AppendStrictSourcesWarning(CommandReturnObject & result,const char * source_name)745b9c1b51eSKate Stone   void AppendStrictSourcesWarning(CommandReturnObject &result,
746b9c1b51eSKate Stone                                   const char *source_name) {
74775930019STodd Fiala     if (!source_name)
74875930019STodd Fiala       return;
74975930019STodd Fiala 
75005097246SAdrian Prantl     // Check if we're *not* using strict sources.  If not, then the user is
75105097246SAdrian Prantl     // going to get debug-level info anyways, probably not what they're
75205097246SAdrian Prantl     // expecting. Unfortunately we can only fix this by adding an env var,
75305097246SAdrian Prantl     // which would have had to have happened already.  Thus, a warning is the
75405097246SAdrian Prantl     // best we can do here.
75575930019STodd Fiala     StreamString stream;
75675930019STodd Fiala     stream.Printf("darwin-log source settings specify to exclude "
75775930019STodd Fiala                   "%s messages, but setting "
75875930019STodd Fiala                   "'plugin.structured-data.darwin-log."
75975930019STodd Fiala                   "strict-sources' is disabled.  This process will "
76075930019STodd Fiala                   "automatically have %s messages included.  Enable"
76175930019STodd Fiala                   " the property and relaunch the target binary to have"
762b9c1b51eSKate Stone                   " these messages excluded.",
763b9c1b51eSKate Stone                   source_name, source_name);
764c156427dSZachary Turner     result.AppendWarning(stream.GetString());
76575930019STodd Fiala   }
76675930019STodd Fiala 
DoExecute(Args & command,CommandReturnObject & result)767b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
76805097246SAdrian Prantl     // First off, set the global sticky state of enable/disable based on this
76905097246SAdrian Prantl     // command execution.
77075930019STodd Fiala     s_is_explicitly_enabled = m_enable;
77175930019STodd Fiala 
77205097246SAdrian Prantl     // Next, if this is an enable, save off the option data. We will need it
77305097246SAdrian Prantl     // later if a process hasn't been launched or attached yet.
774b9c1b51eSKate Stone     if (m_enable) {
77505097246SAdrian Prantl       // Save off enabled configuration so we can apply these parsed options
77605097246SAdrian Prantl       // the next time an attach or launch occurs.
77775930019STodd Fiala       DebuggerSP debugger_sp =
77875930019STodd Fiala           GetCommandInterpreter().GetDebugger().shared_from_this();
77975930019STodd Fiala       SetGlobalEnableOptions(debugger_sp, m_options_sp);
78075930019STodd Fiala     }
78175930019STodd Fiala 
78205097246SAdrian Prantl     // Now check if we have a running process.  If so, we should instruct the
78305097246SAdrian Prantl     // process monitor to enable/disable DarwinLog support now.
784cb2380c9SRaphael Isemann     Target &target = GetSelectedOrDummyTarget();
78575930019STodd Fiala 
78675930019STodd Fiala     // Grab the active process.
787cb2380c9SRaphael Isemann     auto process_sp = target.GetProcessSP();
788b9c1b51eSKate Stone     if (!process_sp) {
78905097246SAdrian Prantl       // No active process, so there is nothing more to do right now.
79075930019STodd Fiala       result.SetStatus(eReturnStatusSuccessFinishNoResult);
79175930019STodd Fiala       return true;
79275930019STodd Fiala     }
79375930019STodd Fiala 
79405097246SAdrian Prantl     // If the process is no longer alive, we can't do this now. We'll catch it
79505097246SAdrian Prantl     // the next time the process is started up.
796b9c1b51eSKate Stone     if (!process_sp->IsAlive()) {
79775930019STodd Fiala       result.SetStatus(eReturnStatusSuccessFinishNoResult);
79875930019STodd Fiala       return true;
79975930019STodd Fiala     }
80075930019STodd Fiala 
80175930019STodd Fiala     // Get the plugin for the process.
80275930019STodd Fiala     auto plugin_sp =
80375930019STodd Fiala         process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
8045f4980f0SPavel Labath     if (!plugin_sp || (plugin_sp->GetPluginName() !=
8055f4980f0SPavel Labath                        StructuredDataDarwinLog::GetStaticPluginName())) {
80675930019STodd Fiala       result.AppendError("failed to get StructuredDataPlugin for "
80775930019STodd Fiala                          "the process");
80875930019STodd Fiala     }
80975930019STodd Fiala     StructuredDataDarwinLog &plugin =
81075930019STodd Fiala         *static_cast<StructuredDataDarwinLog *>(plugin_sp.get());
81175930019STodd Fiala 
812b9c1b51eSKate Stone     if (m_enable) {
81305097246SAdrian Prantl       // Hook up the breakpoint for the process that detects when libtrace has
81405097246SAdrian Prantl       // been sufficiently initialized to really start the os_log stream.  This
81505097246SAdrian Prantl       // is insurance to assure us that logging is really enabled.  Requesting
81605097246SAdrian Prantl       // that logging be enabled for a process before libtrace is initialized
81705097246SAdrian Prantl       // results in a scenario where no errors occur, but no logging is
81805097246SAdrian Prantl       // captured, either.  This step is to eliminate that possibility.
81970355aceSJonas Devlieghere       plugin.AddInitCompletionHook(*process_sp);
82075930019STodd Fiala     }
82175930019STodd Fiala 
82205097246SAdrian Prantl     // Send configuration to the feature by way of the process. Construct the
82305097246SAdrian Prantl     // options we will use.
82475930019STodd Fiala     auto config_sp = m_options_sp->BuildConfigurationData(m_enable);
82597206d57SZachary Turner     const Status error =
826b9c1b51eSKate Stone         process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
82775930019STodd Fiala 
82875930019STodd Fiala     // Report results.
829b9c1b51eSKate Stone     if (!error.Success()) {
83075930019STodd Fiala       result.AppendError(error.AsCString());
83175930019STodd Fiala       // Our configuration failed, so we're definitely disabled.
83275930019STodd Fiala       plugin.SetEnabled(false);
833b9c1b51eSKate Stone     } else {
83475930019STodd Fiala       result.SetStatus(eReturnStatusSuccessFinishNoResult);
8354ebdee0aSBruce Mitchener       // Our configuration succeeded, so we're enabled/disabled per whichever
83605097246SAdrian Prantl       // one this command is setup to do.
83775930019STodd Fiala       plugin.SetEnabled(m_enable);
83875930019STodd Fiala     }
83975930019STodd Fiala     return result.Succeeded();
84075930019STodd Fiala   }
84175930019STodd Fiala 
GetOptions()842b9c1b51eSKate Stone   Options *GetOptions() override {
84375930019STodd Fiala     // We don't have options when this represents disable.
84475930019STodd Fiala     return m_enable ? m_options_sp.get() : nullptr;
84575930019STodd Fiala   }
84675930019STodd Fiala 
84775930019STodd Fiala private:
84875930019STodd Fiala   const bool m_enable;
84975930019STodd Fiala   EnableOptionsSP m_options_sp;
85075930019STodd Fiala };
85175930019STodd Fiala 
85275930019STodd Fiala /// Provides the status command.
853b9c1b51eSKate Stone class StatusCommand : public CommandObjectParsed {
85475930019STodd Fiala public:
StatusCommand(CommandInterpreter & interpreter)855b9c1b51eSKate Stone   StatusCommand(CommandInterpreter &interpreter)
856b9c1b51eSKate Stone       : CommandObjectParsed(interpreter, "status",
85775930019STodd Fiala                             "Show whether Darwin log supported is available"
85875930019STodd Fiala                             " and enabled.",
859b9c1b51eSKate Stone                             "plugin structured-data darwin-log status") {}
86075930019STodd Fiala 
86175930019STodd Fiala protected:
DoExecute(Args & command,CommandReturnObject & result)862b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
86375930019STodd Fiala     auto &stream = result.GetOutputStream();
86475930019STodd Fiala 
86505097246SAdrian Prantl     // Figure out if we've got a process.  If so, we can tell if DarwinLog is
86605097246SAdrian Prantl     // available for that process.
867cb2380c9SRaphael Isemann     Target &target = GetSelectedOrDummyTarget();
868cb2380c9SRaphael Isemann     auto process_sp = target.GetProcessSP();
869cb2380c9SRaphael Isemann     if (!process_sp) {
87075930019STodd Fiala       stream.PutCString("Availability: unknown (requires process)\n");
87175930019STodd Fiala       stream.PutCString("Enabled: not applicable "
87275930019STodd Fiala                         "(requires process)\n");
873b9c1b51eSKate Stone     } else {
87475930019STodd Fiala       auto plugin_sp =
87575930019STodd Fiala           process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
876b9c1b51eSKate Stone       stream.Printf("Availability: %s\n",
877b9c1b51eSKate Stone                     plugin_sp ? "available" : "unavailable");
8785f4980f0SPavel Labath       llvm::StringRef plugin_name = StructuredDataDarwinLog::GetStaticPluginName();
879b9c1b51eSKate Stone       const bool enabled =
8805f4980f0SPavel Labath           plugin_sp ? plugin_sp->GetEnabled(ConstString(plugin_name)) : false;
881b9c1b51eSKate Stone       stream.Printf("Enabled: %s\n", enabled ? "true" : "false");
88275930019STodd Fiala     }
88375930019STodd Fiala 
88475930019STodd Fiala     // Display filter settings.
88575930019STodd Fiala     DebuggerSP debugger_sp =
88675930019STodd Fiala         GetCommandInterpreter().GetDebugger().shared_from_this();
88775930019STodd Fiala     auto options_sp = GetGlobalEnableOptions(debugger_sp);
888b9c1b51eSKate Stone     if (!options_sp) {
88975930019STodd Fiala       // Nothing more to do.
89075930019STodd Fiala       result.SetStatus(eReturnStatusSuccessFinishResult);
89175930019STodd Fiala       return true;
89275930019STodd Fiala     }
89375930019STodd Fiala 
89475930019STodd Fiala     // Print filter rules
89575930019STodd Fiala     stream.PutCString("DarwinLog filter rules:\n");
89675930019STodd Fiala 
89775930019STodd Fiala     stream.IndentMore();
89875930019STodd Fiala 
899b9c1b51eSKate Stone     if (options_sp->GetFilterRules().empty()) {
90075930019STodd Fiala       stream.Indent();
90175930019STodd Fiala       stream.PutCString("none\n");
902b9c1b51eSKate Stone     } else {
90375930019STodd Fiala       // Print each of the filter rules.
90475930019STodd Fiala       int rule_number = 0;
905b9c1b51eSKate Stone       for (auto rule_sp : options_sp->GetFilterRules()) {
90675930019STodd Fiala         ++rule_number;
90775930019STodd Fiala         if (!rule_sp)
90875930019STodd Fiala           continue;
90975930019STodd Fiala 
91075930019STodd Fiala         stream.Indent();
91175930019STodd Fiala         stream.Printf("%02d: ", rule_number);
91275930019STodd Fiala         rule_sp->Dump(stream);
91375930019STodd Fiala         stream.PutChar('\n');
91475930019STodd Fiala       }
91575930019STodd Fiala     }
91675930019STodd Fiala     stream.IndentLess();
91775930019STodd Fiala 
91875930019STodd Fiala     // Print no-match handling.
91975930019STodd Fiala     stream.Indent();
92075930019STodd Fiala     stream.Printf("no-match behavior: %s\n",
921b9c1b51eSKate Stone                   options_sp->GetFallthroughAccepts() ? "accept" : "reject");
92275930019STodd Fiala 
92375930019STodd Fiala     result.SetStatus(eReturnStatusSuccessFinishResult);
92475930019STodd Fiala     return true;
92575930019STodd Fiala   }
92675930019STodd Fiala };
92775930019STodd Fiala 
92875930019STodd Fiala /// Provides the darwin-log base command
929b9c1b51eSKate Stone class BaseCommand : public CommandObjectMultiword {
93075930019STodd Fiala public:
BaseCommand(CommandInterpreter & interpreter)931b9c1b51eSKate Stone   BaseCommand(CommandInterpreter &interpreter)
932b9c1b51eSKate Stone       : CommandObjectMultiword(interpreter, "plugin structured-data darwin-log",
93375930019STodd Fiala                                "Commands for configuring Darwin os_log "
93475930019STodd Fiala                                "support.",
935b9c1b51eSKate Stone                                "") {
93675930019STodd Fiala     // enable
93775930019STodd Fiala     auto enable_help = "Enable Darwin log collection, or re-enable "
93875930019STodd Fiala                        "with modified configuration.";
93975930019STodd Fiala     auto enable_syntax = "plugin structured-data darwin-log enable";
940b9c1b51eSKate Stone     auto enable_cmd_sp = CommandObjectSP(
941b9c1b51eSKate Stone         new EnableCommand(interpreter,
94275930019STodd Fiala                           true, // enable
943b9c1b51eSKate Stone                           "enable", enable_help, enable_syntax));
94475930019STodd Fiala     LoadSubCommand("enable", enable_cmd_sp);
94575930019STodd Fiala 
94675930019STodd Fiala     // disable
94775930019STodd Fiala     auto disable_help = "Disable Darwin log collection.";
94875930019STodd Fiala     auto disable_syntax = "plugin structured-data darwin-log disable";
949b9c1b51eSKate Stone     auto disable_cmd_sp = CommandObjectSP(
950b9c1b51eSKate Stone         new EnableCommand(interpreter,
95175930019STodd Fiala                           false, // disable
952b9c1b51eSKate Stone                           "disable", disable_help, disable_syntax));
95375930019STodd Fiala     LoadSubCommand("disable", disable_cmd_sp);
95475930019STodd Fiala 
95575930019STodd Fiala     // status
956b9c1b51eSKate Stone     auto status_cmd_sp = CommandObjectSP(new StatusCommand(interpreter));
95775930019STodd Fiala     LoadSubCommand("status", status_cmd_sp);
95875930019STodd Fiala   }
95975930019STodd Fiala };
96075930019STodd Fiala 
ParseAutoEnableOptions(Status & error,Debugger & debugger)96197206d57SZachary Turner EnableOptionsSP ParseAutoEnableOptions(Status &error, Debugger &debugger) {
962a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
96305097246SAdrian Prantl   // We are abusing the options data model here so that we can parse options
96405097246SAdrian Prantl   // without requiring the Debugger instance.
96575930019STodd Fiala 
96605097246SAdrian Prantl   // We have an empty execution context at this point.  We only want to parse
96705097246SAdrian Prantl   // options, and we don't need any context to do this here. In fact, we want
96805097246SAdrian Prantl   // to be able to parse the enable options before having any context.
96975930019STodd Fiala   ExecutionContext exe_ctx;
97075930019STodd Fiala 
97175930019STodd Fiala   EnableOptionsSP options_sp(new EnableOptions());
97275930019STodd Fiala   options_sp->NotifyOptionParsingStarting(&exe_ctx);
97375930019STodd Fiala 
974de019b88SJonas Devlieghere   CommandReturnObject result(debugger.GetUseColor());
97575930019STodd Fiala 
97675930019STodd Fiala   // Parse the arguments.
97775930019STodd Fiala   auto options_property_sp =
978b9c1b51eSKate Stone       debugger.GetPropertyValue(nullptr, "plugin.structured-data.darwin-log."
97975930019STodd Fiala                                          "auto-enable-options",
980b9c1b51eSKate Stone                                 false, error);
98175930019STodd Fiala   if (!error.Success())
98275930019STodd Fiala     return EnableOptionsSP();
983b9c1b51eSKate Stone   if (!options_property_sp) {
98475930019STodd Fiala     error.SetErrorString("failed to find option setting for "
98575930019STodd Fiala                          "plugin.structured-data.darwin-log.");
98675930019STodd Fiala     return EnableOptionsSP();
98775930019STodd Fiala   }
98875930019STodd Fiala 
989b9c1b51eSKate Stone   const char *enable_options =
990b9c1b51eSKate Stone       options_property_sp->GetAsString()->GetCurrentValue();
99175930019STodd Fiala   Args args(enable_options);
992b9c1b51eSKate Stone   if (args.GetArgumentCount() > 0) {
99305097246SAdrian Prantl     // Eliminate the initial '--' that would be required to set the settings
99405097246SAdrian Prantl     // that themselves include '-' and/or '--'.
99575930019STodd Fiala     const char *first_arg = args.GetArgumentAtIndex(0);
99675930019STodd Fiala     if (first_arg && (strcmp(first_arg, "--") == 0))
99775930019STodd Fiala       args.Shift();
99875930019STodd Fiala   }
99975930019STodd Fiala 
100075930019STodd Fiala   bool require_validation = false;
10015f56fca4SPavel Labath   llvm::Expected<Args> args_or =
10025f56fca4SPavel Labath       options_sp->Parse(args, &exe_ctx, PlatformSP(), require_validation);
10035f56fca4SPavel Labath   if (!args_or) {
10045f56fca4SPavel Labath     LLDB_LOG_ERROR(
10055f56fca4SPavel Labath         log, args_or.takeError(),
10065f56fca4SPavel Labath         "Parsing plugin.structured-data.darwin-log.auto-enable-options value "
10075f56fca4SPavel Labath         "failed: {0}");
100875930019STodd Fiala     return EnableOptionsSP();
10095f56fca4SPavel Labath   }
101075930019STodd Fiala 
101175930019STodd Fiala   if (!options_sp->VerifyOptions(result))
101275930019STodd Fiala     return EnableOptionsSP();
101375930019STodd Fiala 
101475930019STodd Fiala   // We successfully parsed and validated the options.
101575930019STodd Fiala   return options_sp;
101675930019STodd Fiala }
101775930019STodd Fiala 
RunEnableCommand(CommandInterpreter & interpreter)1018b9c1b51eSKate Stone bool RunEnableCommand(CommandInterpreter &interpreter) {
101975930019STodd Fiala   StreamString command_stream;
102075930019STodd Fiala 
102175930019STodd Fiala   command_stream << "plugin structured-data darwin-log enable";
10223d7161e3SPavel Labath   auto enable_options = GetGlobalProperties().GetAutoEnableOptions();
102331d97a5cSZachary Turner   if (!enable_options.empty()) {
102475930019STodd Fiala     command_stream << ' ';
102575930019STodd Fiala     command_stream << enable_options;
102675930019STodd Fiala   }
102775930019STodd Fiala 
102875930019STodd Fiala   // Run the command.
1029de019b88SJonas Devlieghere   CommandReturnObject return_object(interpreter.GetDebugger().GetUseColor());
1030c156427dSZachary Turner   interpreter.HandleCommand(command_stream.GetData(), eLazyBoolNo,
103175930019STodd Fiala                             return_object);
103275930019STodd Fiala   return return_object.Succeeded();
103375930019STodd Fiala }
103475930019STodd Fiala }
103575930019STodd Fiala using namespace sddarwinlog_private;
103675930019STodd Fiala 
103775930019STodd Fiala #pragma mark -
103875930019STodd Fiala #pragma mark Public static API
103975930019STodd Fiala 
104075930019STodd Fiala // Public static API
104175930019STodd Fiala 
Initialize()1042b9c1b51eSKate Stone void StructuredDataDarwinLog::Initialize() {
104375930019STodd Fiala   RegisterFilterOperations();
1044b9c1b51eSKate Stone   PluginManager::RegisterPlugin(
1045b9c1b51eSKate Stone       GetStaticPluginName(), "Darwin os_log() and os_activity() support",
1046b9c1b51eSKate Stone       &CreateInstance, &DebuggerInitialize, &FilterLaunchInfo);
104775930019STodd Fiala }
104875930019STodd Fiala 
Terminate()1049b9c1b51eSKate Stone void StructuredDataDarwinLog::Terminate() {
105075930019STodd Fiala   PluginManager::UnregisterPlugin(&CreateInstance);
105175930019STodd Fiala }
105275930019STodd Fiala 
105375930019STodd Fiala #pragma mark -
105475930019STodd Fiala #pragma mark StructuredDataPlugin API
105575930019STodd Fiala 
105675930019STodd Fiala // StructuredDataPlugin API
105775930019STodd Fiala 
SupportsStructuredDataType(ConstString type_name)1058b9c1b51eSKate Stone bool StructuredDataDarwinLog::SupportsStructuredDataType(
10590e4c4821SAdrian Prantl     ConstString type_name) {
106075930019STodd Fiala   return type_name == GetDarwinLogTypeName();
106175930019STodd Fiala }
106275930019STodd Fiala 
HandleArrivalOfStructuredData(Process & process,ConstString type_name,const StructuredData::ObjectSP & object_sp)1063b9c1b51eSKate Stone void StructuredDataDarwinLog::HandleArrivalOfStructuredData(
10640e4c4821SAdrian Prantl     Process &process, ConstString type_name,
1065b9c1b51eSKate Stone     const StructuredData::ObjectSP &object_sp) {
1066a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
1067b9c1b51eSKate Stone   if (log) {
106875930019STodd Fiala     StreamString json_stream;
106975930019STodd Fiala     if (object_sp)
107075930019STodd Fiala       object_sp->Dump(json_stream);
107175930019STodd Fiala     else
107275930019STodd Fiala       json_stream.PutCString("<null>");
107363e5fb76SJonas Devlieghere     LLDB_LOGF(log, "StructuredDataDarwinLog::%s() called with json: %s",
1074c156427dSZachary Turner               __FUNCTION__, json_stream.GetData());
107575930019STodd Fiala   }
107675930019STodd Fiala 
107775930019STodd Fiala   // Ignore empty structured data.
1078b9c1b51eSKate Stone   if (!object_sp) {
107963e5fb76SJonas Devlieghere     LLDB_LOGF(log,
108063e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() StructuredData object "
1081b9c1b51eSKate Stone               "is null",
1082b9c1b51eSKate Stone               __FUNCTION__);
108375930019STodd Fiala     return;
108475930019STodd Fiala   }
108575930019STodd Fiala 
108675930019STodd Fiala   // Ignore any data that isn't for us.
1087b9c1b51eSKate Stone   if (type_name != GetDarwinLogTypeName()) {
108863e5fb76SJonas Devlieghere     LLDB_LOGF(log,
108963e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() StructuredData type "
1090b9c1b51eSKate Stone               "expected to be %s but was %s, ignoring",
1091b9c1b51eSKate Stone               __FUNCTION__, GetDarwinLogTypeName().AsCString(),
109275930019STodd Fiala               type_name.AsCString());
109375930019STodd Fiala     return;
109475930019STodd Fiala   }
109575930019STodd Fiala 
109605097246SAdrian Prantl   // Broadcast the structured data event if we have that enabled. This is the
109705097246SAdrian Prantl   // way that the outside world (all clients) get access to this data.  This
109805097246SAdrian Prantl   // plugin sets policy as to whether we do that.
1099b9c1b51eSKate Stone   DebuggerSP debugger_sp = process.GetTarget().GetDebugger().shared_from_this();
110075930019STodd Fiala   auto options_sp = GetGlobalEnableOptions(debugger_sp);
1101b9c1b51eSKate Stone   if (options_sp && options_sp->GetBroadcastEvents()) {
110263e5fb76SJonas Devlieghere     LLDB_LOGF(log, "StructuredDataDarwinLog::%s() broadcasting event",
110375930019STodd Fiala               __FUNCTION__);
110475930019STodd Fiala     process.BroadcastStructuredData(object_sp, shared_from_this());
110575930019STodd Fiala   }
110675930019STodd Fiala 
110705097246SAdrian Prantl   // Later, hang on to a configurable amount of these and allow commands to
110805097246SAdrian Prantl   // inspect, including showing backtraces.
110975930019STodd Fiala }
111075930019STodd Fiala 
SetErrorWithJSON(Status & error,const char * message,StructuredData::Object & object)111197206d57SZachary Turner static void SetErrorWithJSON(Status &error, const char *message,
1112b9c1b51eSKate Stone                              StructuredData::Object &object) {
1113b9c1b51eSKate Stone   if (!message) {
111475930019STodd Fiala     error.SetErrorString("Internal error: message not set.");
111575930019STodd Fiala     return;
111675930019STodd Fiala   }
111775930019STodd Fiala 
111875930019STodd Fiala   StreamString object_stream;
111975930019STodd Fiala   object.Dump(object_stream);
112075930019STodd Fiala   object_stream.Flush();
112175930019STodd Fiala 
1122c156427dSZachary Turner   error.SetErrorStringWithFormat("%s: %s", message, object_stream.GetData());
112375930019STodd Fiala }
112475930019STodd Fiala 
GetDescription(const StructuredData::ObjectSP & object_sp,lldb_private::Stream & stream)112597206d57SZachary Turner Status StructuredDataDarwinLog::GetDescription(
1126b9c1b51eSKate Stone     const StructuredData::ObjectSP &object_sp, lldb_private::Stream &stream) {
112797206d57SZachary Turner   Status error;
112875930019STodd Fiala 
1129b9c1b51eSKate Stone   if (!object_sp) {
113075930019STodd Fiala     error.SetErrorString("No structured data.");
113175930019STodd Fiala     return error;
113275930019STodd Fiala   }
113375930019STodd Fiala 
113475930019STodd Fiala   // Log message payload objects will be dictionaries.
113575930019STodd Fiala   const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
1136b9c1b51eSKate Stone   if (!dictionary) {
113775930019STodd Fiala     SetErrorWithJSON(error, "Structured data should have been a dictionary "
1138b9c1b51eSKate Stone                             "but wasn't",
1139b9c1b51eSKate Stone                      *object_sp);
114075930019STodd Fiala     return error;
114175930019STodd Fiala   }
114275930019STodd Fiala 
114375930019STodd Fiala   // Validate this is really a message for our plugin.
114475930019STodd Fiala   ConstString type_name;
1145b9c1b51eSKate Stone   if (!dictionary->GetValueForKeyAsString("type", type_name)) {
114675930019STodd Fiala     SetErrorWithJSON(error, "Structured data doesn't contain mandatory "
1147b9c1b51eSKate Stone                             "type field",
1148b9c1b51eSKate Stone                      *object_sp);
114975930019STodd Fiala     return error;
115075930019STodd Fiala   }
115175930019STodd Fiala 
1152b9c1b51eSKate Stone   if (type_name != GetDarwinLogTypeName()) {
115375930019STodd Fiala     // This is okay - it simply means the data we received is not a log
115475930019STodd Fiala     // message.  We'll just format it as is.
115575930019STodd Fiala     object_sp->Dump(stream);
115675930019STodd Fiala     return error;
115775930019STodd Fiala   }
115875930019STodd Fiala 
115975930019STodd Fiala   // DarwinLog dictionaries store their data
116075930019STodd Fiala   // in an array with key name "events".
116175930019STodd Fiala   StructuredData::Array *events = nullptr;
1162b9c1b51eSKate Stone   if (!dictionary->GetValueForKeyAsArray("events", events) || !events) {
116375930019STodd Fiala     SetErrorWithJSON(error, "Log structured data is missing mandatory "
1164b9c1b51eSKate Stone                             "'events' field, expected to be an array",
1165b9c1b51eSKate Stone                      *object_sp);
116675930019STodd Fiala     return error;
116775930019STodd Fiala   }
116875930019STodd Fiala 
1169b9c1b51eSKate Stone   events->ForEach(
1170b9c1b51eSKate Stone       [&stream, &error, &object_sp, this](StructuredData::Object *object) {
1171b9c1b51eSKate Stone         if (!object) {
117275930019STodd Fiala           // Invalid.  Stop iterating.
117375930019STodd Fiala           SetErrorWithJSON(error, "Log event entry is null", *object_sp);
117475930019STodd Fiala           return false;
117575930019STodd Fiala         }
117675930019STodd Fiala 
117775930019STodd Fiala         auto event = object->GetAsDictionary();
1178b9c1b51eSKate Stone         if (!event) {
117975930019STodd Fiala           // Invalid, stop iterating.
1180b9c1b51eSKate Stone           SetErrorWithJSON(error, "Log event is not a dictionary", *object_sp);
118175930019STodd Fiala           return false;
118275930019STodd Fiala         }
118375930019STodd Fiala 
118405097246SAdrian Prantl         // If we haven't already grabbed the first timestamp value, do that
118505097246SAdrian Prantl         // now.
1186b9c1b51eSKate Stone         if (!m_recorded_first_timestamp) {
118775930019STodd Fiala           uint64_t timestamp = 0;
1188b9c1b51eSKate Stone           if (event->GetValueForKeyAsInteger("timestamp", timestamp)) {
118975930019STodd Fiala             m_first_timestamp_seen = timestamp;
119075930019STodd Fiala             m_recorded_first_timestamp = true;
119175930019STodd Fiala           }
119275930019STodd Fiala         }
119375930019STodd Fiala 
119475930019STodd Fiala         HandleDisplayOfEvent(*event, stream);
119575930019STodd Fiala         return true;
119675930019STodd Fiala       });
119775930019STodd Fiala 
119875930019STodd Fiala   stream.Flush();
119975930019STodd Fiala   return error;
120075930019STodd Fiala }
120175930019STodd Fiala 
GetEnabled(ConstString type_name) const12020e4c4821SAdrian Prantl bool StructuredDataDarwinLog::GetEnabled(ConstString type_name) const {
12035f4980f0SPavel Labath   if (type_name.GetStringRef() == GetStaticPluginName())
120475930019STodd Fiala     return m_is_enabled;
120575930019STodd Fiala   else
120675930019STodd Fiala     return false;
120775930019STodd Fiala }
120875930019STodd Fiala 
SetEnabled(bool enabled)1209b9c1b51eSKate Stone void StructuredDataDarwinLog::SetEnabled(bool enabled) {
121075930019STodd Fiala   m_is_enabled = enabled;
121175930019STodd Fiala }
121275930019STodd Fiala 
ModulesDidLoad(Process & process,ModuleList & module_list)1213b9c1b51eSKate Stone void StructuredDataDarwinLog::ModulesDidLoad(Process &process,
1214b9c1b51eSKate Stone                                              ModuleList &module_list) {
1215a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
121663e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s called (process uid %u)",
121775930019STodd Fiala             __FUNCTION__, process.GetUniqueID());
121875930019STodd Fiala 
121975930019STodd Fiala   // Check if we should enable the darwin log support on startup/attach.
12203d7161e3SPavel Labath   if (!GetGlobalProperties().GetEnableOnStartup() &&
1221b9c1b51eSKate Stone       !s_is_explicitly_enabled) {
122205097246SAdrian Prantl     // We're neither auto-enabled or explicitly enabled, so we shouldn't try to
122305097246SAdrian Prantl     // enable here.
122463e5fb76SJonas Devlieghere     LLDB_LOGF(log,
122563e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s not applicable, we're not "
1226b9c1b51eSKate Stone               "enabled (process uid %u)",
1227b9c1b51eSKate Stone               __FUNCTION__, process.GetUniqueID());
122875930019STodd Fiala     return;
122975930019STodd Fiala   }
123075930019STodd Fiala 
123175930019STodd Fiala   // If we already added the breakpoint, we've got nothing left to do.
123275930019STodd Fiala   {
123375930019STodd Fiala     std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
1234b9c1b51eSKate Stone     if (m_added_breakpoint) {
123563e5fb76SJonas Devlieghere       LLDB_LOGF(log,
123663e5fb76SJonas Devlieghere                 "StructuredDataDarwinLog::%s process uid %u's "
123775930019STodd Fiala                 "post-libtrace-init breakpoint is already set",
123875930019STodd Fiala                 __FUNCTION__, process.GetUniqueID());
123975930019STodd Fiala       return;
124075930019STodd Fiala     }
124175930019STodd Fiala   }
124275930019STodd Fiala 
124305097246SAdrian Prantl   // The logging support module name, specifies the name of the image name that
124405097246SAdrian Prantl   // must be loaded into the debugged process before we can try to enable
124505097246SAdrian Prantl   // logging.
124675930019STodd Fiala   const char *logging_module_cstr =
12473d7161e3SPavel Labath       GetGlobalProperties().GetLoggingModuleName();
1248b9c1b51eSKate Stone   if (!logging_module_cstr || (logging_module_cstr[0] == 0)) {
124975930019STodd Fiala     // We need this.  Bail.
125063e5fb76SJonas Devlieghere     LLDB_LOGF(log,
125163e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s no logging module name "
125275930019STodd Fiala               "specified, we don't know where to set a breakpoint "
1253b9c1b51eSKate Stone               "(process uid %u)",
1254b9c1b51eSKate Stone               __FUNCTION__, process.GetUniqueID());
125575930019STodd Fiala     return;
125675930019STodd Fiala   }
125775930019STodd Fiala 
1258b9c1b51eSKate Stone   const ConstString logging_module_name = ConstString(logging_module_cstr);
125975930019STodd Fiala 
126075930019STodd Fiala   // We need to see libtrace in the list of modules before we can enable
126175930019STodd Fiala   // tracing for the target process.
126275930019STodd Fiala   bool found_logging_support_module = false;
1263b9c1b51eSKate Stone   for (size_t i = 0; i < module_list.GetSize(); ++i) {
126475930019STodd Fiala     auto module_sp = module_list.GetModuleAtIndex(i);
126575930019STodd Fiala     if (!module_sp)
126675930019STodd Fiala       continue;
126775930019STodd Fiala 
126875930019STodd Fiala     auto &file_spec = module_sp->GetFileSpec();
126975930019STodd Fiala     found_logging_support_module =
127075930019STodd Fiala         (file_spec.GetLastPathComponent() == logging_module_name);
127175930019STodd Fiala     if (found_logging_support_module)
127275930019STodd Fiala       break;
127375930019STodd Fiala   }
127475930019STodd Fiala 
1275b9c1b51eSKate Stone   if (!found_logging_support_module) {
127663e5fb76SJonas Devlieghere     LLDB_LOGF(log,
127763e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s logging module %s "
127875930019STodd Fiala               "has not yet been loaded, can't set a breakpoint "
1279b9c1b51eSKate Stone               "yet (process uid %u)",
1280b9c1b51eSKate Stone               __FUNCTION__, logging_module_name.AsCString(),
128175930019STodd Fiala               process.GetUniqueID());
128275930019STodd Fiala     return;
128375930019STodd Fiala   }
128475930019STodd Fiala 
128505097246SAdrian Prantl   // Time to enqueue the breakpoint so we can wait for logging support to be
128605097246SAdrian Prantl   // initialized before we try to tap the libtrace stream.
128775930019STodd Fiala   AddInitCompletionHook(process);
128863e5fb76SJonas Devlieghere   LLDB_LOGF(log,
128963e5fb76SJonas Devlieghere             "StructuredDataDarwinLog::%s post-init hook breakpoint "
1290b9c1b51eSKate Stone             "set for logging module %s (process uid %u)",
1291b9c1b51eSKate Stone             __FUNCTION__, logging_module_name.AsCString(),
129275930019STodd Fiala             process.GetUniqueID());
129375930019STodd Fiala 
129405097246SAdrian Prantl   // We need to try the enable here as well, which will succeed in the event
129505097246SAdrian Prantl   // that we're attaching to (rather than launching) the process and the
129605097246SAdrian Prantl   // process is already past initialization time.  In that case, the completion
129705097246SAdrian Prantl   // breakpoint will never get hit and therefore won't start that way.  It
129805097246SAdrian Prantl   // doesn't hurt much beyond a bit of bandwidth if we end up doing this twice.
129905097246SAdrian Prantl   // It hurts much more if we don't get the logging enabled when the user
130005097246SAdrian Prantl   // expects it.
130175930019STodd Fiala   EnableNow();
130275930019STodd Fiala }
130375930019STodd Fiala 
130438267274SJason Molenda // public destructor
130538267274SJason Molenda 
~StructuredDataDarwinLog()130638267274SJason Molenda StructuredDataDarwinLog::~StructuredDataDarwinLog() {
130738267274SJason Molenda   if (m_breakpoint_id != LLDB_INVALID_BREAK_ID) {
130838267274SJason Molenda     ProcessSP process_sp(GetProcess());
130938267274SJason Molenda     if (process_sp) {
131038267274SJason Molenda       process_sp->GetTarget().RemoveBreakpointByID(m_breakpoint_id);
131138267274SJason Molenda       m_breakpoint_id = LLDB_INVALID_BREAK_ID;
131238267274SJason Molenda     }
131338267274SJason Molenda   }
131438267274SJason Molenda }
131538267274SJason Molenda 
131675930019STodd Fiala #pragma mark -
131775930019STodd Fiala #pragma mark Private instance methods
131875930019STodd Fiala 
131975930019STodd Fiala // Private constructors
132075930019STodd Fiala 
StructuredDataDarwinLog(const ProcessWP & process_wp)1321b9c1b51eSKate Stone StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp)
1322b9c1b51eSKate Stone     : StructuredDataPlugin(process_wp), m_recorded_first_timestamp(false),
1323b9c1b51eSKate Stone       m_first_timestamp_seen(0), m_is_enabled(false),
132438267274SJason Molenda       m_added_breakpoint_mutex(), m_added_breakpoint(),
132538267274SJason Molenda       m_breakpoint_id(LLDB_INVALID_BREAK_ID) {}
132675930019STodd Fiala 
132775930019STodd Fiala // Private static methods
132875930019STodd Fiala 
132975930019STodd Fiala StructuredDataPluginSP
CreateInstance(Process & process)1330b9c1b51eSKate Stone StructuredDataDarwinLog::CreateInstance(Process &process) {
133105097246SAdrian Prantl   // Currently only Apple targets support the os_log/os_activity protocol.
133275930019STodd Fiala   if (process.GetTarget().GetArchitecture().GetTriple().getVendor() ==
1333b9c1b51eSKate Stone       llvm::Triple::VendorType::Apple) {
133475930019STodd Fiala     auto process_wp = ProcessWP(process.shared_from_this());
133575930019STodd Fiala     return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp));
1336b9c1b51eSKate Stone   } else {
133775930019STodd Fiala     return StructuredDataPluginSP();
133875930019STodd Fiala   }
133975930019STodd Fiala }
134075930019STodd Fiala 
DebuggerInitialize(Debugger & debugger)1341b9c1b51eSKate Stone void StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger) {
134275930019STodd Fiala   // Setup parent class first.
134375930019STodd Fiala   StructuredDataPlugin::InitializeBasePluginForDebugger(debugger);
134475930019STodd Fiala 
134575930019STodd Fiala   // Get parent command.
134675930019STodd Fiala   auto &interpreter = debugger.GetCommandInterpreter();
1347a01bccdbSZachary Turner   llvm::StringRef parent_command_text = "plugin structured-data";
134875930019STodd Fiala   auto parent_command =
134975930019STodd Fiala       interpreter.GetCommandObjectForCommand(parent_command_text);
1350b9c1b51eSKate Stone   if (!parent_command) {
135175930019STodd Fiala     // Ut oh, parent failed to create parent command.
135275930019STodd Fiala     // TODO log
135375930019STodd Fiala     return;
135475930019STodd Fiala   }
135575930019STodd Fiala 
135675930019STodd Fiala   auto command_name = "darwin-log";
135775930019STodd Fiala   auto command_sp = CommandObjectSP(new BaseCommand(interpreter));
135875930019STodd Fiala   bool result = parent_command->LoadSubCommand(command_name, command_sp);
1359b9c1b51eSKate Stone   if (!result) {
136075930019STodd Fiala     // TODO log it once we setup structured data logging
136175930019STodd Fiala   }
136275930019STodd Fiala 
1363b9c1b51eSKate Stone   if (!PluginManager::GetSettingForPlatformPlugin(
1364b9c1b51eSKate Stone           debugger, StructuredDataDarwinLogProperties::GetSettingName())) {
136575930019STodd Fiala     const bool is_global_setting = true;
1366b9c1b51eSKate Stone     PluginManager::CreateSettingForStructuredDataPlugin(
13673d7161e3SPavel Labath         debugger, GetGlobalProperties().GetValueProperties(),
136875930019STodd Fiala         ConstString("Properties for the darwin-log"
136975930019STodd Fiala                     " plug-in."),
137075930019STodd Fiala         is_global_setting);
137175930019STodd Fiala   }
137275930019STodd Fiala }
137375930019STodd Fiala 
FilterLaunchInfo(ProcessLaunchInfo & launch_info,Target * target)137497206d57SZachary Turner Status StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info,
1375b9c1b51eSKate Stone                                                  Target *target) {
137697206d57SZachary Turner   Status error;
137775930019STodd Fiala 
137805097246SAdrian Prantl   // If we're not debugging this launched process, there's nothing for us to do
137905097246SAdrian Prantl   // here.
138075930019STodd Fiala   if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug))
138175930019STodd Fiala     return error;
138275930019STodd Fiala 
138375930019STodd Fiala   // Darwin os_log() support automatically adds debug-level and info-level
138475930019STodd Fiala   // messages when a debugger is attached to a process.  However, with
13854ebdee0aSBruce Mitchener   // integrated support for debugging built into the command-line LLDB, the
138605097246SAdrian Prantl   // user may specifically set to *not* include debug-level and info-level
138705097246SAdrian Prantl   // content.  When the user is using the integrated log support, we want to
138805097246SAdrian Prantl   // put the kabosh on that automatic adding of info and debug level. This is
138905097246SAdrian Prantl   // done by adding an environment variable to the process on launch. (This
139005097246SAdrian Prantl   // also means it is not possible to suppress this behavior if attaching to an
139105097246SAdrian Prantl   // already-running app).
1392a007a6d8SPavel Labath   // Log *log = GetLog(LLDBLog::Platform);
139375930019STodd Fiala 
139475930019STodd Fiala   // If the target architecture is not one that supports DarwinLog, we have
139575930019STodd Fiala   // nothing to do here.
1396b9c1b51eSKate Stone   auto &triple = target ? target->GetArchitecture().GetTriple()
1397b9c1b51eSKate Stone                         : launch_info.GetArchitecture().GetTriple();
1398b9c1b51eSKate Stone   if (triple.getVendor() != llvm::Triple::Apple) {
139975930019STodd Fiala     return error;
140075930019STodd Fiala   }
140175930019STodd Fiala 
140205097246SAdrian Prantl   // If DarwinLog is not enabled (either by explicit user command or via the
140305097246SAdrian Prantl   // auto-enable option), then we have nothing to do.
14043d7161e3SPavel Labath   if (!GetGlobalProperties().GetEnableOnStartup() &&
1405b9c1b51eSKate Stone       !s_is_explicitly_enabled) {
140675930019STodd Fiala     // Nothing to do, DarwinLog is not enabled.
140775930019STodd Fiala     return error;
140875930019STodd Fiala   }
140975930019STodd Fiala 
141005097246SAdrian Prantl   // If we don't have parsed configuration info, that implies we have enable-
141105097246SAdrian Prantl   // on-startup set up, but we haven't yet attempted to run the enable command.
1412b9c1b51eSKate Stone   if (!target) {
141305097246SAdrian Prantl     // We really can't do this without a target.  We need to be able to get to
141405097246SAdrian Prantl     // the debugger to get the proper options to do this right.
141575930019STodd Fiala     // TODO log.
141675930019STodd Fiala     error.SetErrorString("requires a target to auto-enable DarwinLog.");
141775930019STodd Fiala     return error;
141875930019STodd Fiala   }
141975930019STodd Fiala 
142075930019STodd Fiala   DebuggerSP debugger_sp = target->GetDebugger().shared_from_this();
142175930019STodd Fiala   auto options_sp = GetGlobalEnableOptions(debugger_sp);
1422b9c1b51eSKate Stone   if (!options_sp && debugger_sp) {
142375930019STodd Fiala     options_sp = ParseAutoEnableOptions(error, *debugger_sp.get());
142475930019STodd Fiala     if (!options_sp || !error.Success())
142575930019STodd Fiala       return error;
142675930019STodd Fiala 
142705097246SAdrian Prantl     // We already parsed the options, save them now so we don't generate them
142805097246SAdrian Prantl     // again until the user runs the command manually.
142975930019STodd Fiala     SetGlobalEnableOptions(debugger_sp, options_sp);
143075930019STodd Fiala   }
143175930019STodd Fiala 
1432b9c1b51eSKate Stone   if (!options_sp->GetEchoToStdErr()) {
143305097246SAdrian Prantl     // The user doesn't want to see os_log/NSLog messages echo to stderr. That
143405097246SAdrian Prantl     // mechanism is entirely separate from the DarwinLog support. By default we
143505097246SAdrian Prantl     // don't want to get it via stderr, because that would be in duplicate of
143605097246SAdrian Prantl     // the explicit log support here.
143775930019STodd Fiala 
143875930019STodd Fiala     // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
143975930019STodd Fiala     // echoing of os_log()/NSLog() to stderr in the target program.
144062930e57SPavel Labath     launch_info.GetEnvironment().erase("OS_ACTIVITY_DT_MODE");
144175930019STodd Fiala 
144205097246SAdrian Prantl     // We will also set the env var that tells any downstream launcher from
144305097246SAdrian Prantl     // adding OS_ACTIVITY_DT_MODE.
144462930e57SPavel Labath     launch_info.GetEnvironment()["IDE_DISABLED_OS_ACTIVITY_DT_MODE"] = "1";
144575930019STodd Fiala   }
144675930019STodd Fiala 
144705097246SAdrian Prantl   // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable debug and
144805097246SAdrian Prantl   // info level messages.
144975930019STodd Fiala   const char *env_var_value;
145075930019STodd Fiala   if (options_sp->GetIncludeDebugLevel())
145175930019STodd Fiala     env_var_value = "debug";
145275930019STodd Fiala   else if (options_sp->GetIncludeInfoLevel())
145375930019STodd Fiala     env_var_value = "info";
145475930019STodd Fiala   else
14557ffdb155STodd Fiala     env_var_value = "default";
145675930019STodd Fiala 
145762930e57SPavel Labath   launch_info.GetEnvironment()["OS_ACTIVITY_MODE"] = env_var_value;
145875930019STodd Fiala 
145975930019STodd Fiala   return error;
146075930019STodd Fiala }
146175930019STodd Fiala 
InitCompletionHookCallback(void * baton,StoppointCallbackContext * context,lldb::user_id_t break_id,lldb::user_id_t break_loc_id)1462b9c1b51eSKate Stone bool StructuredDataDarwinLog::InitCompletionHookCallback(
1463b9c1b51eSKate Stone     void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
1464b9c1b51eSKate Stone     lldb::user_id_t break_loc_id) {
146575930019STodd Fiala   // We hit the init function.  We now want to enqueue our new thread plan,
146675930019STodd Fiala   // which will in turn enqueue a StepOut thread plan. When the StepOut
146705097246SAdrian Prantl   // finishes and control returns to our new thread plan, that is the time when
146805097246SAdrian Prantl   // we can execute our logic to enable the logging support.
146975930019STodd Fiala 
1470a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
147163e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s() called", __FUNCTION__);
147275930019STodd Fiala 
147375930019STodd Fiala   // Get the current thread.
1474b9c1b51eSKate Stone   if (!context) {
147563e5fb76SJonas Devlieghere     LLDB_LOGF(log,
147663e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: no context, "
1477b9c1b51eSKate Stone               "ignoring",
1478b9c1b51eSKate Stone               __FUNCTION__);
147975930019STodd Fiala     return false;
148075930019STodd Fiala   }
148175930019STodd Fiala 
148275930019STodd Fiala   // Get the plugin from the process.
148375930019STodd Fiala   auto process_sp = context->exe_ctx_ref.GetProcessSP();
1484b9c1b51eSKate Stone   if (!process_sp) {
148563e5fb76SJonas Devlieghere     LLDB_LOGF(log,
148663e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: invalid "
1487b9c1b51eSKate Stone               "process in context, ignoring",
1488b9c1b51eSKate Stone               __FUNCTION__);
148975930019STodd Fiala     return false;
149075930019STodd Fiala   }
149163e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s() call is for process uid %d",
149275930019STodd Fiala             __FUNCTION__, process_sp->GetUniqueID());
149375930019STodd Fiala 
1494b9c1b51eSKate Stone   auto plugin_sp = process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
1495b9c1b51eSKate Stone   if (!plugin_sp) {
149663e5fb76SJonas Devlieghere     LLDB_LOGF(log,
149763e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: no plugin for "
149875930019STodd Fiala               "feature %s in process uid %u",
149975930019STodd Fiala               __FUNCTION__, GetDarwinLogTypeName().AsCString(),
150075930019STodd Fiala               process_sp->GetUniqueID());
150175930019STodd Fiala     return false;
150275930019STodd Fiala   }
150375930019STodd Fiala 
150475930019STodd Fiala   // Create the callback for when the thread plan completes.
150575930019STodd Fiala   bool called_enable_method = false;
150675930019STodd Fiala   const auto process_uid = process_sp->GetUniqueID();
150775930019STodd Fiala 
150875930019STodd Fiala   std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp);
150975930019STodd Fiala   ThreadPlanCallOnFunctionExit::Callback callback =
151075930019STodd Fiala       [plugin_wp, &called_enable_method, log, process_uid]() {
151163e5fb76SJonas Devlieghere         LLDB_LOGF(log,
151263e5fb76SJonas Devlieghere                   "StructuredDataDarwinLog::post-init callback: "
1513b9c1b51eSKate Stone                   "called (process uid %u)",
1514b9c1b51eSKate Stone                   process_uid);
151575930019STodd Fiala 
151675930019STodd Fiala         auto strong_plugin_sp = plugin_wp.lock();
1517b9c1b51eSKate Stone         if (!strong_plugin_sp) {
151863e5fb76SJonas Devlieghere           LLDB_LOGF(log,
151963e5fb76SJonas Devlieghere                     "StructuredDataDarwinLog::post-init callback: "
152075930019STodd Fiala                     "plugin no longer exists, ignoring (process "
1521b9c1b51eSKate Stone                     "uid %u)",
1522b9c1b51eSKate Stone                     process_uid);
152375930019STodd Fiala           return;
152475930019STodd Fiala         }
152505097246SAdrian Prantl         // Make sure we only call it once, just in case the thread plan hits
152605097246SAdrian Prantl         // the breakpoint twice.
1527b9c1b51eSKate Stone         if (!called_enable_method) {
152863e5fb76SJonas Devlieghere           LLDB_LOGF(log,
152963e5fb76SJonas Devlieghere                     "StructuredDataDarwinLog::post-init callback: "
153075930019STodd Fiala                     "calling EnableNow() (process uid %u)",
153175930019STodd Fiala                     process_uid);
153275930019STodd Fiala           static_cast<StructuredDataDarwinLog *>(strong_plugin_sp.get())
153375930019STodd Fiala               ->EnableNow();
153475930019STodd Fiala           called_enable_method = true;
1535b9c1b51eSKate Stone         } else {
153605097246SAdrian Prantl           // Our breakpoint was hit more than once.  Unexpected but no harm
153705097246SAdrian Prantl           // done.  Log it.
153863e5fb76SJonas Devlieghere           LLDB_LOGF(log,
153963e5fb76SJonas Devlieghere                     "StructuredDataDarwinLog::post-init callback: "
154075930019STodd Fiala                     "skipping EnableNow(), already called by "
154175930019STodd Fiala                     "callback [we hit this more than once] "
1542b9c1b51eSKate Stone                     "(process uid %u)",
1543b9c1b51eSKate Stone                     process_uid);
154475930019STodd Fiala         }
154575930019STodd Fiala       };
154675930019STodd Fiala 
154775930019STodd Fiala   // Grab the current thread.
154875930019STodd Fiala   auto thread_sp = context->exe_ctx_ref.GetThreadSP();
1549b9c1b51eSKate Stone   if (!thread_sp) {
155063e5fb76SJonas Devlieghere     LLDB_LOGF(log,
155163e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: failed to "
155275930019STodd Fiala               "retrieve the current thread from the execution "
155375930019STodd Fiala               "context, nowhere to run the thread plan (process uid "
1554b9c1b51eSKate Stone               "%u)",
1555b9c1b51eSKate Stone               __FUNCTION__, process_sp->GetUniqueID());
155675930019STodd Fiala     return false;
155775930019STodd Fiala   }
155875930019STodd Fiala 
155975930019STodd Fiala   // Queue the thread plan.
156070355aceSJonas Devlieghere   auto thread_plan_sp =
156170355aceSJonas Devlieghere       ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp, callback));
156275930019STodd Fiala   const bool abort_other_plans = false;
156375930019STodd Fiala   thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans);
156463e5fb76SJonas Devlieghere   LLDB_LOGF(log,
156563e5fb76SJonas Devlieghere             "StructuredDataDarwinLog::%s() queuing thread plan on "
156675930019STodd Fiala             "trace library init method entry (process uid %u)",
156775930019STodd Fiala             __FUNCTION__, process_sp->GetUniqueID());
156875930019STodd Fiala 
156975930019STodd Fiala   // We return false here to indicate that it isn't a public stop.
157075930019STodd Fiala   return false;
157175930019STodd Fiala }
157275930019STodd Fiala 
AddInitCompletionHook(Process & process)1573b9c1b51eSKate Stone void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
1574a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
157563e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s() called (process uid %u)",
157675930019STodd Fiala             __FUNCTION__, process.GetUniqueID());
157775930019STodd Fiala 
157875930019STodd Fiala   // Make sure we haven't already done this.
157975930019STodd Fiala   {
158075930019STodd Fiala     std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
1581b9c1b51eSKate Stone     if (m_added_breakpoint) {
158263e5fb76SJonas Devlieghere       LLDB_LOGF(log,
158363e5fb76SJonas Devlieghere                 "StructuredDataDarwinLog::%s() ignoring request, "
158475930019STodd Fiala                 "breakpoint already set (process uid %u)",
158575930019STodd Fiala                 __FUNCTION__, process.GetUniqueID());
158675930019STodd Fiala       return;
158775930019STodd Fiala     }
158875930019STodd Fiala 
158975930019STodd Fiala     // We're about to do this, don't let anybody else try to do it.
159075930019STodd Fiala     m_added_breakpoint = true;
159175930019STodd Fiala   }
159275930019STodd Fiala 
159305097246SAdrian Prantl   // Set a breakpoint for the process that will kick in when libtrace has
159405097246SAdrian Prantl   // finished its initialization.
159575930019STodd Fiala   Target &target = process.GetTarget();
159675930019STodd Fiala 
159775930019STodd Fiala   // Build up the module list.
159875930019STodd Fiala   FileSpecList module_spec_list;
159975930019STodd Fiala   auto module_file_spec =
16003d7161e3SPavel Labath       FileSpec(GetGlobalProperties().GetLoggingModuleName());
160175930019STodd Fiala   module_spec_list.Append(module_file_spec);
160275930019STodd Fiala 
160375930019STodd Fiala   // We aren't specifying a source file set.
160475930019STodd Fiala   FileSpecList *source_spec_list = nullptr;
160575930019STodd Fiala 
160675930019STodd Fiala   const char *func_name = "_libtrace_init";
160775930019STodd Fiala   const lldb::addr_t offset = 0;
160875930019STodd Fiala   const LazyBool skip_prologue = eLazyBoolCalculate;
160975930019STodd Fiala   // This is an internal breakpoint - the user shouldn't see it.
161075930019STodd Fiala   const bool internal = true;
161175930019STodd Fiala   const bool hardware = false;
161275930019STodd Fiala 
1613b9c1b51eSKate Stone   auto breakpoint_sp = target.CreateBreakpoint(
1614b9c1b51eSKate Stone       &module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull,
1615b9c1b51eSKate Stone       eLanguageTypeC, offset, skip_prologue, internal, hardware);
1616b9c1b51eSKate Stone   if (!breakpoint_sp) {
161775930019STodd Fiala     // Huh?  Bail here.
161863e5fb76SJonas Devlieghere     LLDB_LOGF(log,
161963e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() failed to set "
162075930019STodd Fiala               "breakpoint in module %s, function %s (process uid %u)",
16213d7161e3SPavel Labath               __FUNCTION__, GetGlobalProperties().GetLoggingModuleName(),
162275930019STodd Fiala               func_name, process.GetUniqueID());
162375930019STodd Fiala     return;
162475930019STodd Fiala   }
162575930019STodd Fiala 
162675930019STodd Fiala   // Set our callback.
162775930019STodd Fiala   breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr);
162838267274SJason Molenda   m_breakpoint_id = breakpoint_sp->GetID();
162963e5fb76SJonas Devlieghere   LLDB_LOGF(log,
163063e5fb76SJonas Devlieghere             "StructuredDataDarwinLog::%s() breakpoint set in module %s,"
163175930019STodd Fiala             "function %s (process uid %u)",
16323d7161e3SPavel Labath             __FUNCTION__, GetGlobalProperties().GetLoggingModuleName(),
163375930019STodd Fiala             func_name, process.GetUniqueID());
163475930019STodd Fiala }
163575930019STodd Fiala 
DumpTimestamp(Stream & stream,uint64_t timestamp)1636b9c1b51eSKate Stone void StructuredDataDarwinLog::DumpTimestamp(Stream &stream,
1637b9c1b51eSKate Stone                                             uint64_t timestamp) {
163875930019STodd Fiala   const uint64_t delta_nanos = timestamp - m_first_timestamp_seen;
163975930019STodd Fiala 
164075930019STodd Fiala   const uint64_t hours = delta_nanos / NANOS_PER_HOUR;
164175930019STodd Fiala   uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR;
164275930019STodd Fiala 
164375930019STodd Fiala   const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE;
164475930019STodd Fiala   nanos_remaining = nanos_remaining % NANOS_PER_MINUTE;
164575930019STodd Fiala 
164675930019STodd Fiala   const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND;
164775930019STodd Fiala   nanos_remaining = nanos_remaining % NANOS_PER_SECOND;
164875930019STodd Fiala 
1649b9c1b51eSKate Stone   stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64, hours,
1650b9c1b51eSKate Stone                 minutes, seconds, nanos_remaining);
165175930019STodd Fiala }
165275930019STodd Fiala 
165375930019STodd Fiala size_t
DumpHeader(Stream & output_stream,const StructuredData::Dictionary & event)165475930019STodd Fiala StructuredDataDarwinLog::DumpHeader(Stream &output_stream,
1655b9c1b51eSKate Stone                                     const StructuredData::Dictionary &event) {
165675930019STodd Fiala   StreamString stream;
165775930019STodd Fiala 
165875930019STodd Fiala   ProcessSP process_sp = GetProcess();
1659b9c1b51eSKate Stone   if (!process_sp) {
166075930019STodd Fiala     // TODO log
166175930019STodd Fiala     return 0;
166275930019STodd Fiala   }
166375930019STodd Fiala 
166475930019STodd Fiala   DebuggerSP debugger_sp =
166575930019STodd Fiala       process_sp->GetTarget().GetDebugger().shared_from_this();
1666b9c1b51eSKate Stone   if (!debugger_sp) {
166775930019STodd Fiala     // TODO log
166875930019STodd Fiala     return 0;
166975930019STodd Fiala   }
167075930019STodd Fiala 
167175930019STodd Fiala   auto options_sp = GetGlobalEnableOptions(debugger_sp);
1672b9c1b51eSKate Stone   if (!options_sp) {
167375930019STodd Fiala     // TODO log
167475930019STodd Fiala     return 0;
167575930019STodd Fiala   }
167675930019STodd Fiala 
167775930019STodd Fiala   // Check if we should even display a header.
167875930019STodd Fiala   if (!options_sp->GetDisplayAnyHeaderFields())
167975930019STodd Fiala     return 0;
168075930019STodd Fiala 
168175930019STodd Fiala   stream.PutChar('[');
168275930019STodd Fiala 
168375930019STodd Fiala   int header_count = 0;
1684b9c1b51eSKate Stone   if (options_sp->GetDisplayTimestampRelative()) {
168575930019STodd Fiala     uint64_t timestamp = 0;
1686b9c1b51eSKate Stone     if (event.GetValueForKeyAsInteger("timestamp", timestamp)) {
168775930019STodd Fiala       DumpTimestamp(stream, timestamp);
168875930019STodd Fiala       ++header_count;
168975930019STodd Fiala     }
169075930019STodd Fiala   }
169175930019STodd Fiala 
1692b9c1b51eSKate Stone   if (options_sp->GetDisplayActivityChain()) {
16932833321fSZachary Turner     llvm::StringRef activity_chain;
169475930019STodd Fiala     if (event.GetValueForKeyAsString("activity-chain", activity_chain) &&
1695b9c1b51eSKate Stone         !activity_chain.empty()) {
169675930019STodd Fiala       if (header_count > 0)
169775930019STodd Fiala         stream.PutChar(',');
169875930019STodd Fiala 
169905097246SAdrian Prantl       // Display the activity chain, from parent-most to child-most activity,
170005097246SAdrian Prantl       // separated by a colon (:).
170175930019STodd Fiala       stream.PutCString("activity-chain=");
1702771ef6d4SMalcolm Parsons       stream.PutCString(activity_chain);
170375930019STodd Fiala       ++header_count;
170475930019STodd Fiala     }
170575930019STodd Fiala   }
170675930019STodd Fiala 
1707b9c1b51eSKate Stone   if (options_sp->GetDisplaySubsystem()) {
17082833321fSZachary Turner     llvm::StringRef subsystem;
1709b9c1b51eSKate Stone     if (event.GetValueForKeyAsString("subsystem", subsystem) &&
1710b9c1b51eSKate Stone         !subsystem.empty()) {
171175930019STodd Fiala       if (header_count > 0)
171275930019STodd Fiala         stream.PutChar(',');
171375930019STodd Fiala       stream.PutCString("subsystem=");
1714771ef6d4SMalcolm Parsons       stream.PutCString(subsystem);
171575930019STodd Fiala       ++header_count;
171675930019STodd Fiala     }
171775930019STodd Fiala   }
171875930019STodd Fiala 
1719b9c1b51eSKate Stone   if (options_sp->GetDisplayCategory()) {
17202833321fSZachary Turner     llvm::StringRef category;
1721b9c1b51eSKate Stone     if (event.GetValueForKeyAsString("category", category) &&
1722b9c1b51eSKate Stone         !category.empty()) {
172375930019STodd Fiala       if (header_count > 0)
172475930019STodd Fiala         stream.PutChar(',');
172575930019STodd Fiala       stream.PutCString("category=");
1726771ef6d4SMalcolm Parsons       stream.PutCString(category);
172775930019STodd Fiala       ++header_count;
172875930019STodd Fiala     }
172975930019STodd Fiala   }
173075930019STodd Fiala   stream.PutCString("] ");
173175930019STodd Fiala 
1732c156427dSZachary Turner   output_stream.PutCString(stream.GetString());
173375930019STodd Fiala 
1734c156427dSZachary Turner   return stream.GetSize();
173575930019STodd Fiala }
173675930019STodd Fiala 
HandleDisplayOfEvent(const StructuredData::Dictionary & event,Stream & stream)1737b9c1b51eSKate Stone size_t StructuredDataDarwinLog::HandleDisplayOfEvent(
1738b9c1b51eSKate Stone     const StructuredData::Dictionary &event, Stream &stream) {
173975930019STodd Fiala   // Check the type of the event.
174075930019STodd Fiala   ConstString event_type;
1741b9c1b51eSKate Stone   if (!event.GetValueForKeyAsString("type", event_type)) {
174205097246SAdrian Prantl     // Hmm, we expected to get events that describe what they are.  Continue
174305097246SAdrian Prantl     // anyway.
174475930019STodd Fiala     return 0;
174575930019STodd Fiala   }
174675930019STodd Fiala 
174775930019STodd Fiala   if (event_type != GetLogEventType())
174875930019STodd Fiala     return 0;
174975930019STodd Fiala 
175075930019STodd Fiala   size_t total_bytes = 0;
175175930019STodd Fiala 
175275930019STodd Fiala   // Grab the message content.
17532833321fSZachary Turner   llvm::StringRef message;
175475930019STodd Fiala   if (!event.GetValueForKeyAsString("message", message))
175575930019STodd Fiala     return true;
175675930019STodd Fiala 
175775930019STodd Fiala   // Display the log entry.
17582833321fSZachary Turner   const auto len = message.size();
175975930019STodd Fiala 
176075930019STodd Fiala   total_bytes += DumpHeader(stream, event);
176175930019STodd Fiala 
17622833321fSZachary Turner   stream.Write(message.data(), len);
176375930019STodd Fiala   total_bytes += len;
176475930019STodd Fiala 
176575930019STodd Fiala   // Add an end of line.
176675930019STodd Fiala   stream.PutChar('\n');
176775930019STodd Fiala   total_bytes += sizeof(char);
176875930019STodd Fiala 
176975930019STodd Fiala   return total_bytes;
177075930019STodd Fiala }
177175930019STodd Fiala 
EnableNow()1772b9c1b51eSKate Stone void StructuredDataDarwinLog::EnableNow() {
1773a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Process);
177463e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s() called", __FUNCTION__);
177575930019STodd Fiala 
177675930019STodd Fiala   // Run the enable command.
177775930019STodd Fiala   auto process_sp = GetProcess();
1778b9c1b51eSKate Stone   if (!process_sp) {
177975930019STodd Fiala     // Nothing to do.
178063e5fb76SJonas Devlieghere     LLDB_LOGF(log,
178163e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: failed to get "
1782b9c1b51eSKate Stone               "valid process, skipping",
1783b9c1b51eSKate Stone               __FUNCTION__);
178475930019STodd Fiala     return;
178575930019STodd Fiala   }
178663e5fb76SJonas Devlieghere   LLDB_LOGF(log, "StructuredDataDarwinLog::%s() call is for process uid %u",
178775930019STodd Fiala             __FUNCTION__, process_sp->GetUniqueID());
178875930019STodd Fiala 
178905097246SAdrian Prantl   // If we have configuration data, we can directly enable it now. Otherwise,
179005097246SAdrian Prantl   // we need to run through the command interpreter to parse the auto-run
179105097246SAdrian Prantl   // options (which is the only way we get here without having already-parsed
179205097246SAdrian Prantl   // configuration data).
179375930019STodd Fiala   DebuggerSP debugger_sp =
179475930019STodd Fiala       process_sp->GetTarget().GetDebugger().shared_from_this();
1795b9c1b51eSKate Stone   if (!debugger_sp) {
179663e5fb76SJonas Devlieghere     LLDB_LOGF(log,
179763e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: failed to get "
179875930019STodd Fiala               "debugger shared pointer, skipping (process uid %u)",
179975930019STodd Fiala               __FUNCTION__, process_sp->GetUniqueID());
180075930019STodd Fiala     return;
180175930019STodd Fiala   }
180275930019STodd Fiala 
180375930019STodd Fiala   auto options_sp = GetGlobalEnableOptions(debugger_sp);
1804b9c1b51eSKate Stone   if (!options_sp) {
180505097246SAdrian Prantl     // We haven't run the enable command yet.  Just do that now, it'll take
180605097246SAdrian Prantl     // care of the rest.
180775930019STodd Fiala     auto &interpreter = debugger_sp->GetCommandInterpreter();
180875930019STodd Fiala     const bool success = RunEnableCommand(interpreter);
1809b9c1b51eSKate Stone     if (log) {
181075930019STodd Fiala       if (success)
181163e5fb76SJonas Devlieghere         LLDB_LOGF(log,
181263e5fb76SJonas Devlieghere                   "StructuredDataDarwinLog::%s() ran enable command "
181375930019STodd Fiala                   "successfully for (process uid %u)",
181475930019STodd Fiala                   __FUNCTION__, process_sp->GetUniqueID());
181575930019STodd Fiala       else
181663e5fb76SJonas Devlieghere         LLDB_LOGF(log,
181763e5fb76SJonas Devlieghere                   "StructuredDataDarwinLog::%s() error: running "
181875930019STodd Fiala                   "enable command failed (process uid %u)",
181975930019STodd Fiala                   __FUNCTION__, process_sp->GetUniqueID());
182075930019STodd Fiala     }
1821*2fc38b2bSJonas Devlieghere     Debugger::ReportError("failed to configure DarwinLog support",
1822*2fc38b2bSJonas Devlieghere                           debugger_sp->GetID());
182375930019STodd Fiala     return;
182475930019STodd Fiala   }
182575930019STodd Fiala 
182605097246SAdrian Prantl   // We've previously been enabled. We will re-enable now with the previously
182705097246SAdrian Prantl   // specified options.
182875930019STodd Fiala   auto config_sp = options_sp->BuildConfigurationData(true);
1829b9c1b51eSKate Stone   if (!config_sp) {
183063e5fb76SJonas Devlieghere     LLDB_LOGF(log,
183163e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() warning: failed to "
183275930019STodd Fiala               "build configuration data for enable options, skipping "
1833b9c1b51eSKate Stone               "(process uid %u)",
1834b9c1b51eSKate Stone               __FUNCTION__, process_sp->GetUniqueID());
183575930019STodd Fiala     return;
183675930019STodd Fiala   }
183775930019STodd Fiala 
183875930019STodd Fiala   // We can run it directly.
183975930019STodd Fiala   // Send configuration to the feature by way of the process.
184097206d57SZachary Turner   const Status error =
1841b9c1b51eSKate Stone       process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
184275930019STodd Fiala 
184375930019STodd Fiala   // Report results.
1844b9c1b51eSKate Stone   if (!error.Success()) {
184563e5fb76SJonas Devlieghere     LLDB_LOGF(log,
184663e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() "
184775930019STodd Fiala               "ConfigureStructuredData() call failed "
184875930019STodd Fiala               "(process uid %u): %s",
1849b9c1b51eSKate Stone               __FUNCTION__, process_sp->GetUniqueID(), error.AsCString());
1850*2fc38b2bSJonas Devlieghere     Debugger::ReportError("failed to configure DarwinLog support",
1851*2fc38b2bSJonas Devlieghere                           debugger_sp->GetID());
185275930019STodd Fiala     m_is_enabled = false;
1853b9c1b51eSKate Stone   } else {
185475930019STodd Fiala     m_is_enabled = true;
185563e5fb76SJonas Devlieghere     LLDB_LOGF(log,
185663e5fb76SJonas Devlieghere               "StructuredDataDarwinLog::%s() success via direct "
1857b9c1b51eSKate Stone               "configuration (process uid %u)",
1858b9c1b51eSKate Stone               __FUNCTION__, process_sp->GetUniqueID());
185975930019STodd Fiala   }
186075930019STodd Fiala }
1861