1 //===-- StructuredDataDarwinLog.cpp -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "StructuredDataDarwinLog.h"
11 
12 // C includes
13 #include <string.h>
14 
15 // C++ includes
16 #include <sstream>
17 
18 #include "lldb/Breakpoint/StoppointCallbackContext.h"
19 #include "lldb/Core/Debugger.h"
20 #include "lldb/Core/Log.h"
21 #include "lldb/Core/Module.h"
22 #include "lldb/Core/PluginManager.h"
23 #include "lldb/Core/RegularExpression.h"
24 #include "lldb/Interpreter/CommandInterpreter.h"
25 #include "lldb/Interpreter/CommandObjectMultiword.h"
26 #include "lldb/Interpreter/CommandReturnObject.h"
27 #include "lldb/Interpreter/OptionValueProperties.h"
28 #include "lldb/Interpreter/OptionValueString.h"
29 #include "lldb/Interpreter/Property.h"
30 #include "lldb/Target/Process.h"
31 #include "lldb/Target/Target.h"
32 #include "lldb/Target/ThreadPlanCallOnFunctionExit.h"
33 
34 #define DARWIN_LOG_TYPE_VALUE "DarwinLog"
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 
39 #pragma mark -
40 #pragma mark Anonymous Namespace
41 
42 // -----------------------------------------------------------------------------
43 // Anonymous namespace
44 // -----------------------------------------------------------------------------
45 
46 namespace sddarwinlog_private
47 {
48     const uint64_t NANOS_PER_MICRO = 1000;
49     const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000;
50     const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
51     const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60;
52     const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60;
53 
54     static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true;
55 
56     //------------------------------------------------------------------
57     /// Global, sticky enable switch.  If true, the user has explicitly
58     /// run the enable command.  When a process launches or is attached to,
59     /// we will enable DarwinLog if either the settings for auto-enable is
60     /// on, or if the user had explicitly run enable at some point prior
61     /// to the launch/attach.
62     //------------------------------------------------------------------
63     static bool s_is_explicitly_enabled;
64 
65     class EnableOptions;
66     using EnableOptionsSP = std::shared_ptr<EnableOptions>;
67 
68     using OptionsMap = std::map<DebuggerWP, EnableOptionsSP,
69                                 std::owner_less<DebuggerWP>>;
70 
71     static OptionsMap&
72     GetGlobalOptionsMap()
73     {
74         static OptionsMap s_options_map;
75         return s_options_map;
76     }
77 
78     static std::mutex&
79     GetGlobalOptionsMapLock()
80     {
81         static std::mutex s_options_map_lock;
82         return s_options_map_lock;
83     }
84 
85     EnableOptionsSP
86     GetGlobalEnableOptions(const DebuggerSP &debugger_sp)
87     {
88         if (!debugger_sp)
89             return EnableOptionsSP();
90 
91         std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
92         OptionsMap &options_map = GetGlobalOptionsMap();
93         DebuggerWP debugger_wp(debugger_sp);
94         auto find_it = options_map.find(debugger_wp);
95         if (find_it != options_map.end())
96             return find_it->second;
97         else
98             return EnableOptionsSP();
99     }
100 
101     void
102     SetGlobalEnableOptions(const DebuggerSP &debugger_sp,
103                            const EnableOptionsSP &options_sp)
104     {
105         std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
106         OptionsMap &options_map = GetGlobalOptionsMap();
107         DebuggerWP debugger_wp(debugger_sp);
108         auto find_it = options_map.find(debugger_wp);
109         if (find_it != options_map.end())
110             find_it->second = options_sp;
111         else
112             options_map.insert(std::make_pair(debugger_wp, options_sp));
113     }
114 
115 #pragma mark -
116 #pragma mark Settings Handling
117 
118     //------------------------------------------------------------------
119     /// Code to handle the StructuredDataDarwinLog settings
120     //------------------------------------------------------------------
121 
122     static PropertyDefinition
123     g_properties[] =
124     {
125         {
126             "enable-on-startup" ,        // name
127             OptionValue::eTypeBoolean, // type
128             true,                      // global
129             false,                     // default uint value
130             nullptr,                   // default cstring value
131             nullptr,                   // enum values
132             "Enable Darwin os_log collection when debugged process is launched "
133             "or attached."             // description
134         },
135         {
136             "auto-enable-options" ,    // name
137             OptionValue::eTypeString,  // type
138             true,                      // global
139             0,                         // default uint value
140             "",                        // default cstring value
141             nullptr,                   // enum values
142             "Specify the options to 'plugin structured-data darwin-log enable' "
143             "that should be applied when automatically enabling logging on "
144             "startup/attach."          // description
145         },
146         // Last entry sentinel.
147         {
148             nullptr,
149             OptionValue::eTypeInvalid,
150             false,
151             0  ,
152             nullptr,
153             nullptr,
154             nullptr
155         }
156     };
157 
158     enum {
159         ePropertyEnableOnStartup = 0,
160         ePropertyAutoEnableOptions = 1
161     };
162 
163     class StructuredDataDarwinLogProperties : public Properties
164     {
165     public:
166 
167         static ConstString &
168         GetSettingName ()
169         {
170             static ConstString g_setting_name("darwin-log");
171             return g_setting_name;
172         }
173 
174         StructuredDataDarwinLogProperties() :
175         Properties ()
176         {
177             m_collection_sp.reset(new OptionValueProperties(GetSettingName()));
178             m_collection_sp->Initialize(g_properties);
179         }
180 
181         virtual
182         ~StructuredDataDarwinLogProperties()
183         {
184         }
185 
186         bool
187         GetEnableOnStartup() const
188         {
189             const uint32_t idx = ePropertyEnableOnStartup;
190             return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx,
191                                      g_properties[idx].default_uint_value != 0);
192         }
193 
194         const char*
195         GetAutoEnableOptions() const
196         {
197             const uint32_t idx = ePropertyAutoEnableOptions;
198             return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx,
199                                           g_properties[idx].default_cstr_value);
200         }
201 
202         const char*
203         GetLoggingModuleName() const
204         {
205             return "libsystem_trace.dylib";
206         }
207     };
208 
209     using StructuredDataDarwinLogPropertiesSP =
210     std::shared_ptr<StructuredDataDarwinLogProperties>;
211 
212     static const StructuredDataDarwinLogPropertiesSP&
213     GetGlobalProperties()
214     {
215         static StructuredDataDarwinLogPropertiesSP g_settings_sp;
216         if (!g_settings_sp)
217             g_settings_sp.reset(new StructuredDataDarwinLogProperties());
218         return g_settings_sp;
219     }
220 
221     const char *const s_filter_attributes[] =
222     {
223         "activity",       // current activity
224         "activity-chain", // entire activity chain, each level separated by ':'
225         "category",       // category of the log message
226         "message",        // message contents, fully expanded
227         "subsystem"       // subsystem of the log message
228 
229         // Consider impelmenting this action as it would be cheaper to filter.
230         // "message" requires always formatting the message, which is a waste
231         // of cycles if it ends up being rejected.
232         // "format",      // format string used to format message text
233     };
234 
235     static const ConstString&
236     GetDarwinLogTypeName()
237     {
238         static const ConstString s_key_name("DarwinLog");
239         return s_key_name;
240     }
241 
242     static const ConstString&
243     GetLogEventType()
244     {
245         static const ConstString s_event_type("log");
246         return s_event_type;
247     }
248 
249     class FilterRule;
250     using FilterRuleSP = std::shared_ptr<FilterRule>;
251 
252     class FilterRule
253     {
254     public:
255 
256         virtual
257         ~FilterRule()
258         {
259         }
260 
261         using OperationCreationFunc =
262             std::function<FilterRuleSP(bool accept,
263                                        size_t attribute_index,
264                                        const std::string &op_arg,
265                                        Error &error)>;
266 
267         static void
268         RegisterOperation(const ConstString &operation,
269                           const OperationCreationFunc &creation_func)
270         {
271             GetCreationFuncMap().insert(std::make_pair(operation,
272                                                        creation_func));
273         }
274 
275         static
276         FilterRuleSP CreateRule(bool match_accepts, size_t attribute,
277                                 const ConstString &operation,
278                                 const std::string &op_arg, Error &error)
279         {
280             // Find the creation func for this type of filter rule.
281             auto map = GetCreationFuncMap();
282             auto find_it = map.find(operation);
283             if (find_it == map.end())
284             {
285                 error.SetErrorStringWithFormat("unknown filter operation \""
286                                                "%s\"",
287                                                operation.GetCString());
288                 return FilterRuleSP();
289             }
290 
291             return find_it->second(match_accepts, attribute, op_arg, error);
292         }
293 
294         StructuredData::ObjectSP Serialize() const
295         {
296             StructuredData::Dictionary *dict_p =
297                 new StructuredData::Dictionary();
298 
299             // Indicate whether this is an accept or reject rule.
300             dict_p->AddBooleanItem("accept", m_accept);
301 
302             // Indicate which attribute of the message this filter references.
303             // This can drop into the rule-specific DoSerialization if we get
304             // to the point where not all FilterRule derived classes work on
305             // an attribute.  (e.g. logical and/or and other compound
306             // operations).
307             dict_p->AddStringItem("attribute",
308                                   s_filter_attributes[m_attribute_index]);
309 
310             // Indicate the type of the rule.
311             dict_p->AddStringItem("type", GetOperationType().GetCString());
312 
313             // Let the rule add its own specific details here.
314             DoSerialization(*dict_p);
315 
316             return StructuredData::ObjectSP(dict_p);
317         }
318 
319         virtual void
320         Dump(Stream &stream) const = 0;
321 
322         const ConstString&
323         GetOperationType() const
324         {
325             return m_operation;
326         }
327 
328     protected:
329 
330         FilterRule(bool accept, size_t attribute_index,
331                    const ConstString &operation) :
332             m_accept(accept),
333             m_attribute_index(attribute_index),
334             m_operation(operation)
335         {
336         }
337 
338         virtual void
339         DoSerialization(StructuredData::Dictionary &dict) const = 0;
340 
341         bool
342         GetMatchAccepts() const
343         {
344             return m_accept;
345         }
346 
347         const char*
348         GetFilterAttribute() const
349         {
350             return s_filter_attributes[m_attribute_index];
351         }
352 
353     private:
354 
355         using CreationFuncMap = std::map<ConstString, OperationCreationFunc>;
356 
357         static CreationFuncMap&
358         GetCreationFuncMap()
359         {
360             static CreationFuncMap s_map;
361             return s_map;
362         }
363 
364         const bool m_accept;
365         const size_t m_attribute_index;
366         const ConstString m_operation;
367 
368     };
369 
370     using FilterRules = std::vector<FilterRuleSP>;
371 
372     class RegexFilterRule : public FilterRule
373     {
374     public:
375 
376         static void
377         RegisterOperation()
378         {
379             FilterRule::RegisterOperation(StaticGetOperation(),
380                                           CreateOperation);
381         }
382 
383         void
384         Dump(Stream &stream) const override
385         {
386             stream.Printf("%s %s regex %s",
387                           GetMatchAccepts() ? "accept" : "reject",
388                           GetFilterAttribute(), m_regex_text.c_str());
389         }
390 
391     protected:
392 
393         void
394         DoSerialization(StructuredData::Dictionary &dict) const override
395         {
396             dict.AddStringItem("regex", m_regex_text);
397         }
398 
399     private:
400 
401         static FilterRuleSP
402         CreateOperation(bool accept, size_t attribute_index,
403                         const std::string &op_arg, Error &error)
404         {
405             // We treat the op_arg as a regex.  Validate it.
406             if (op_arg.empty())
407             {
408                 error.SetErrorString("regex filter type requires a regex "
409                                      "argument");
410                 return FilterRuleSP();
411             }
412 
413             // Instantiate the regex so we can report any errors.
414             auto regex = RegularExpression(op_arg.c_str());
415             if (!regex.IsValid())
416             {
417                 char error_text[256];
418                 error_text[0] = '\0';
419                 regex.GetErrorAsCString(error_text, sizeof(error_text));
420                 error.SetErrorString(error_text);
421                 return FilterRuleSP();
422             }
423 
424             // We passed all our checks, this appears fine.
425             error.Clear();
426             return FilterRuleSP(new RegexFilterRule(accept, attribute_index,
427                                                     op_arg));
428         }
429 
430         static const ConstString &
431         StaticGetOperation()
432         {
433             static ConstString s_operation("regex");
434             return s_operation;
435         }
436 
437         RegexFilterRule(bool accept, size_t attribute_index,
438                         const std::string &regex_text) :
439             FilterRule(accept, attribute_index, StaticGetOperation()),
440             m_regex_text(regex_text)
441         {
442         }
443 
444         const std::string m_regex_text;
445     };
446 
447     class ExactMatchFilterRule : public FilterRule
448     {
449     public:
450 
451         static void
452         RegisterOperation()
453         {
454             FilterRule::RegisterOperation(StaticGetOperation(),
455                                           CreateOperation);
456         }
457 
458         void
459         Dump(Stream &stream) const override
460         {
461             stream.Printf("%s %s match %s",
462                           GetMatchAccepts() ? "accept" : "reject",
463                           GetFilterAttribute(), m_match_text.c_str());
464         }
465 
466     protected:
467 
468         void
469         DoSerialization(StructuredData::Dictionary &dict) const override
470         {
471             dict.AddStringItem("exact_text", m_match_text);
472         }
473 
474     private:
475 
476         static FilterRuleSP
477         CreateOperation(bool accept, size_t attribute_index,
478                         const std::string &op_arg, Error &error)
479         {
480             if (op_arg.empty())
481             {
482                 error.SetErrorString("exact match filter type requires an "
483                                      "argument containing the text that must "
484                                      "match the specified message attribute.");
485                 return FilterRuleSP();
486             }
487 
488             error.Clear();
489             return FilterRuleSP(new ExactMatchFilterRule(accept,
490                                                          attribute_index,
491                                                          op_arg));
492         }
493 
494         static const ConstString &
495         StaticGetOperation()
496         {
497             static ConstString s_operation("match");
498             return s_operation;
499         }
500 
501         ExactMatchFilterRule(bool accept, size_t attribute_index,
502                              const std::string &match_text) :
503             FilterRule(accept, attribute_index, StaticGetOperation()),
504             m_match_text(match_text)
505         {
506         }
507 
508         const std::string m_match_text;
509     };
510 
511     static void
512     RegisterFilterOperations()
513     {
514         ExactMatchFilterRule::RegisterOperation();
515         RegexFilterRule::RegisterOperation();
516     }
517 
518     // =========================================================================
519     // Commands
520     // =========================================================================
521 
522     // -------------------------------------------------------------------------
523     /// Provides the main on-off switch for enabling darwin logging.
524     ///
525     /// It is valid to run the enable command when logging is already enabled.
526     /// This resets the logging with whatever settings are currently set.
527     // -------------------------------------------------------------------------
528     class EnableOptions : public Options
529     {
530     public:
531 
532         EnableOptions() :
533             Options(),
534             m_include_debug_level(false),
535             m_include_info_level(false),
536             m_include_any_process(false),
537             m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS),
538             m_echo_to_stderr(false),
539             m_display_timestamp_relative(false),
540             m_display_subsystem(false),
541             m_display_category(false),
542             m_display_activity_chain(false),
543             m_broadcast_events(true),
544             m_live_stream(true),
545             m_filter_rules()
546         {
547         }
548 
549         void
550         OptionParsingStarting (ExecutionContext *execution_context) override
551         {
552             m_include_debug_level = false;
553             m_include_info_level = false;
554             m_include_any_process = false;
555             m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS;
556             m_echo_to_stderr = false;
557             m_display_timestamp_relative = false;
558             m_display_subsystem = false;
559             m_display_category = false;
560             m_display_activity_chain = false;
561             m_broadcast_events = true;
562             m_live_stream = true;
563             m_filter_rules.clear();
564         }
565 
566         Error
567         SetOptionValue (uint32_t option_idx, const char *option_arg,
568                         ExecutionContext *execution_context) override
569         {
570             Error error;
571 
572             const int short_option = m_getopt_table[option_idx].val;
573             switch (short_option)
574             {
575                 case 'a':
576                     m_include_any_process = true;
577                     break;
578 
579                 case 'A':
580                     m_display_timestamp_relative = true;
581                     m_display_category = true;
582                     m_display_subsystem = true;
583                     m_display_activity_chain = true;
584                     break;
585 
586                 case 'b':
587                     m_broadcast_events =
588                         Args::StringToBoolean(option_arg, true, nullptr);
589                     break;
590 
591                 case 'c':
592                     m_display_category = true;
593                     break;
594 
595                 case 'C':
596                     m_display_activity_chain = true;
597                     break;
598 
599                 case 'd':
600                     m_include_debug_level = true;
601                     break;
602 
603                 case 'e':
604                     m_echo_to_stderr =
605                         Args::StringToBoolean(option_arg, false, nullptr);
606                     break;
607 
608                 case 'f':
609                     return ParseFilterRule(option_arg);
610 
611                 case 'i':
612                     m_include_info_level = true;
613                     break;
614 
615                 case 'l':
616                     m_live_stream =
617                         Args::StringToBoolean(option_arg, false, nullptr);
618                     break;
619 
620                 case 'n':
621                     m_filter_fall_through_accepts =
622                         Args::StringToBoolean(option_arg, true, nullptr);
623                     break;
624 
625                 case 'r':
626                     m_display_timestamp_relative = true;
627                     break;
628 
629                 case 's':
630                     m_display_subsystem = true;
631                     break;
632 
633                 default:
634                     error.SetErrorStringWithFormat("unsupported option '%c'",
635                                                    short_option);
636             }
637             return error;
638         }
639 
640         const OptionDefinition*
641         GetDefinitions () override
642         {
643             return g_enable_option_table;
644         }
645 
646         StructuredData::DictionarySP
647         BuildConfigurationData(bool enabled)
648         {
649             StructuredData::DictionarySP config_sp(new StructuredData::
650                                                    Dictionary());
651 
652             // Set the basic enabled state.
653             config_sp->AddBooleanItem("enabled", enabled);
654 
655             // If we're disabled, there's nothing more to add.
656             if (!enabled)
657                 return config_sp;
658 
659             // Handle source stream flags.
660             auto source_flags_sp =
661                 StructuredData::DictionarySP(new StructuredData::Dictionary());
662             config_sp->AddItem("source-flags", source_flags_sp);
663 
664             source_flags_sp->AddBooleanItem("any-process",
665                                             m_include_any_process);
666             source_flags_sp->AddBooleanItem("debug-level",
667                                             m_include_debug_level);
668             // The debug-level flag, if set, implies info-level.
669             source_flags_sp->AddBooleanItem("info-level",
670                                             m_include_info_level ||
671                                             m_include_debug_level);
672             source_flags_sp->AddBooleanItem("live-stream",
673                                             m_live_stream);
674 
675             // Specify default filter rule (the fall-through)
676             config_sp->AddBooleanItem("filter-fall-through-accepts",
677                                       m_filter_fall_through_accepts);
678 
679 
680             // Handle filter rules
681             if (!m_filter_rules.empty())
682             {
683                 auto json_filter_rules_sp =
684                     StructuredData::ArraySP(new StructuredData::Array);
685                 config_sp->AddItem("filter-rules",
686                                    json_filter_rules_sp);
687                 for (auto &rule_sp : m_filter_rules)
688                 {
689                     if (!rule_sp)
690                         continue;
691                     json_filter_rules_sp->AddItem(rule_sp->Serialize());
692                 }
693             }
694             return config_sp;
695         }
696 
697         bool
698         GetIncludeDebugLevel() const
699         {
700             return m_include_debug_level;
701         }
702 
703         bool
704         GetIncludeInfoLevel() const
705         {
706             // Specifying debug level implies info level.
707             return m_include_info_level || m_include_debug_level;
708         }
709 
710         const FilterRules&
711         GetFilterRules() const
712         {
713             return m_filter_rules;
714         }
715 
716         bool
717         GetFallthroughAccepts() const
718         {
719             return m_filter_fall_through_accepts;
720         }
721 
722         bool
723         GetEchoToStdErr() const
724         {
725             return m_echo_to_stderr;
726         }
727 
728         bool
729         GetDisplayTimestampRelative() const
730         {
731             return m_display_timestamp_relative;
732         }
733 
734         bool
735         GetDisplaySubsystem() const
736         {
737             return m_display_subsystem;
738         }
739         bool
740         GetDisplayCategory() const
741         {
742             return m_display_category;
743         }
744         bool
745         GetDisplayActivityChain() const
746         {
747             return m_display_activity_chain;
748         }
749 
750         bool
751         GetDisplayAnyHeaderFields() const
752         {
753             return
754                 m_display_timestamp_relative ||
755                 m_display_activity_chain ||
756                 m_display_subsystem ||
757                 m_display_category;
758         }
759 
760         bool
761         GetBroadcastEvents() const
762         {
763             return m_broadcast_events;
764         }
765 
766     private:
767 
768         Error
769         ParseFilterRule(const char *rule_text_cstr)
770         {
771             Error error;
772 
773             if (!rule_text_cstr || !rule_text_cstr[0])
774             {
775                 error.SetErrorString("invalid rule_text");
776                 return error;
777             }
778 
779             // filter spec format:
780             //
781             // {action} {attribute} {op}
782             //
783             // {action} :=
784             //   accept |
785             //   reject
786             //
787             // {attribute} :=
788             //   category       |
789             //   subsystem      |
790             //   activity       |
791             //   activity-chain |
792             //   message        |
793             //   format
794             //
795             // {op} :=
796             //   match {exact-match-text} |
797             //   regex {search-regex}
798 
799             const std::string rule_text(rule_text_cstr);
800 
801             // Parse action.
802             auto action_end_pos = rule_text.find(" ");
803             if (action_end_pos == std::string::npos)
804             {
805                 error.SetErrorStringWithFormat("could not parse filter rule "
806                                                "action from \"%s\"",
807                                                rule_text_cstr);
808                 return error;
809             }
810             auto action = rule_text.substr(0, action_end_pos);
811             bool accept;
812             if (action == "accept")
813                 accept = true;
814             else if (action == "reject")
815                 accept = false;
816             else
817             {
818                 error.SetErrorString("filter action must be \"accept\" or "
819                                      "\"deny\"");
820                 return error;
821             }
822 
823             // parse attribute
824             auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1);
825             if (attribute_end_pos == std::string::npos)
826             {
827                 error.SetErrorStringWithFormat("could not parse filter rule "
828                                                "attribute from \"%s\"",
829                                                rule_text_cstr);
830                 return error;
831             }
832             auto attribute =
833                 rule_text.substr(action_end_pos + 1,
834                                  attribute_end_pos - (action_end_pos + 1));
835             auto attribute_index = MatchAttributeIndex(attribute);
836             if (attribute_index < 0)
837             {
838                 error.SetErrorStringWithFormat("filter rule attribute unknown: "
839                                                "%s", attribute.c_str());
840                 return error;
841             }
842 
843             // parse operation
844             auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1);
845             auto operation =
846                 rule_text.substr(attribute_end_pos + 1,
847                                  operation_end_pos - (attribute_end_pos + 1));
848 
849             // add filter spec
850             auto rule_sp =
851             FilterRule::CreateRule(accept, attribute_index,
852                                    ConstString(operation),
853                                    rule_text.substr(operation_end_pos + 1),
854                                    error);
855 
856             if (rule_sp && error.Success())
857                 m_filter_rules.push_back(rule_sp);
858 
859             return error;
860         }
861 
862         int
863         MatchAttributeIndex(const std::string &attribute_name)
864         {
865             auto attribute_count =
866                 sizeof(s_filter_attributes) / sizeof(s_filter_attributes[0]);
867             for (size_t i = 0; i < attribute_count; ++i)
868             {
869                 if (attribute_name == s_filter_attributes[i])
870                     return static_cast<int>(i);
871             }
872 
873             // We didn't match anything.
874             return -1;
875         }
876 
877         static OptionDefinition g_enable_option_table[];
878 
879         bool         m_include_debug_level;
880         bool         m_include_info_level;
881         bool         m_include_any_process;
882         bool         m_filter_fall_through_accepts;
883         bool         m_echo_to_stderr;
884         bool         m_display_timestamp_relative;
885         bool         m_display_subsystem;
886         bool         m_display_category;
887         bool         m_display_activity_chain;
888         bool         m_broadcast_events;
889         bool         m_live_stream;
890         FilterRules  m_filter_rules;
891     };
892 
893     OptionDefinition
894     EnableOptions::g_enable_option_table[] =
895     {
896         // Source stream include/exclude options (the first-level filter).
897         // This one should be made as small as possible as everything that
898         // goes through here must be processed by the process monitor.
899         { LLDB_OPT_SET_ALL, false, "any-process", 'a',
900             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
901             "Specifies log messages from other related processes should be "
902             "included." },
903         { LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument,
904             nullptr, nullptr, 0, eArgTypeNone,
905             "Specifies debug-level log messages should be included.  Specifying"
906             " --debug implies --info."},
907         { LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument,
908             nullptr, nullptr, 0, eArgTypeNone,
909             "Specifies info-level log messages should be included." },
910         { LLDB_OPT_SET_ALL, false, "filter", 'f',
911             OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgRawInput,
912             // There doesn't appear to be a great way for me to have these
913             // multi-line, formatted tables in help.  This looks mostly right
914             // but there are extra linefeeds added at seemingly random spots,
915             // and indentation isn't handled properly on those lines.
916             "Appends a filter rule to the log message filter chain.  Multiple "
917             "rules may be added by specifying this option multiple times, "
918             "once per filter rule.  Filter rules are processed in the order "
919             "they are specified, with the --no-match-accepts setting used "
920             "for any message that doesn't match one of the rules.\n"
921             "\n"
922             "    Filter spec format:\n"
923             "\n"
924             "    --filter \"{action} {attribute} {op}\"\n"
925             "\n"
926             "    {action} :=\n"
927             "      accept |\n"
928             "      reject\n"
929             "\n"
930             "    {attribute} :=\n"
931             "       activity       |  // message's most-derived activity\n"
932             "       activity-chain |  // message's {parent}:{child} activity\n"
933             "       category       |  // message's category\n"
934             "       message        |  // message's expanded contents\n"
935             "       subsystem      |  // message's subsystem\n"
936             "\n"
937             "    {op} :=\n"
938             "      match {exact-match-text} |\n"
939             "      regex {search-regex}\n"
940             "\n"
941             "The regex flavor used is the C++ std::regex ECMAScript format.  "
942             "Prefer character classes like [[:digit:]] to \\d and the like, as "
943             "getting the backslashes escaped through properly is error-prone."
944         },
945         { LLDB_OPT_SET_ALL, false, "live-stream", 'l',
946             OptionParser::eRequiredArgument, nullptr, nullptr, 0,
947             eArgTypeBoolean,
948             "Specify whether logging events are live-streamed or buffered.  "
949             "True indicates live streaming, false indicates buffered.  The "
950             "default is true (live streaming).  Live streaming will deliver "
951             "log messages with less delay, but buffered capture mode has less "
952             "of an observer effect." },
953         { LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n',
954             OptionParser::eRequiredArgument, nullptr, nullptr, 0,
955             eArgTypeBoolean,
956             "Specify whether a log message that doesn't match any filter rule "
957             "is accepted or rejected, where true indicates accept.  The "
958             "default is true." },
959         { LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e',
960             OptionParser::eRequiredArgument, nullptr, nullptr, 0,
961             eArgTypeBoolean,
962             "Specify whether os_log()/NSLog() messages are echoed to the "
963             "target program's stderr.  When DarwinLog is enabled, we shut off "
964             "the mirroring of os_log()/NSLog() to the program's stderr.  "
965             "Setting this flag to true will restore the stderr mirroring."
966             "The default is false." },
967         { LLDB_OPT_SET_ALL, false, "broadcast-events", 'b',
968             OptionParser::eRequiredArgument, nullptr, nullptr, 0,
969             eArgTypeBoolean,
970             "Specify if the plugin should broadcast events.  Broadcasting "
971             "log events is a requirement for displaying the log entries in "
972             "LLDB command-line.  It is also required if LLDB clients want to "
973             "process log events.  The default is true." },
974         // Message formatting options
975         { LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r',
976             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
977             "Include timestamp in the message header when printing a log "
978             "message.  The timestamp is relative to the first displayed "
979             "message." },
980         { LLDB_OPT_SET_ALL, false, "subsystem", 's',
981             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
982             "Include the subsystem in the the message header when displaying "
983             "a log message." },
984         { LLDB_OPT_SET_ALL, false, "category", 'c',
985             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
986             "Include the category in the the message header when displaying "
987             "a log message." },
988         { LLDB_OPT_SET_ALL, false, "activity-chain", 'C',
989             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
990             "Include the activity parent-child chain in the the message header "
991             "when displaying a log message.  The activity hierarchy is "
992             "displayed as {grandparent-activity}:"
993             "{parent-activity}:{activity}[:...]."},
994         { LLDB_OPT_SET_ALL, false, "all-fields", 'A',
995             OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone,
996             "Shortcut to specify that all header fields should be displayed." },
997 
998         // Tail sentinel entry
999         { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
1000     };
1001 
1002     class EnableCommand : public CommandObjectParsed
1003     {
1004     public:
1005 
1006         EnableCommand(CommandInterpreter &interpreter, bool enable,
1007                                const char *name, const char *help,
1008                                const char *syntax) :
1009             CommandObjectParsed(interpreter, name, help, syntax),
1010             m_enable(enable),
1011             m_options_sp(enable ? new EnableOptions() : nullptr)
1012         {
1013         }
1014 
1015     protected:
1016 
1017         void
1018         AppendStrictSourcesWarning(CommandReturnObject &result,
1019                                    const char *source_name)
1020         {
1021             if (!source_name)
1022                 return;
1023 
1024             // Check if we're *not* using strict sources.  If not,
1025             // then the user is going to get debug-level info
1026             // anyways, probably not what they're expecting.
1027             // Unfortunately we can only fix this by adding an
1028             // env var, which would have had to have happened
1029             // already.  Thus, a warning is the best we can do here.
1030             StreamString stream;
1031             stream.Printf("darwin-log source settings specify to exclude "
1032                           "%s messages, but setting "
1033                           "'plugin.structured-data.darwin-log."
1034                           "strict-sources' is disabled.  This process will "
1035                           "automatically have %s messages included.  Enable"
1036                           " the property and relaunch the target binary to have"
1037                           " these messages excluded.", source_name,
1038                           source_name);
1039             result.AppendWarning(stream.GetString().c_str());
1040         }
1041 
1042         bool
1043         DoExecute (Args& command, CommandReturnObject &result) override
1044         {
1045             // First off, set the global sticky state of enable/disable
1046             // based on this command execution.
1047             s_is_explicitly_enabled = m_enable;
1048 
1049             // Next, if this is an enable, save off the option data.
1050             // We will need it later if a process hasn't been launched or
1051             // attached yet.
1052             if (m_enable)
1053             {
1054                 // Save off enabled configuration so we can apply these parsed
1055                 // options the next time an attach or launch occurs.
1056                 DebuggerSP debugger_sp =
1057                     GetCommandInterpreter().GetDebugger().shared_from_this();
1058                 SetGlobalEnableOptions(debugger_sp, m_options_sp);
1059             }
1060 
1061             // Now check if we have a running process.  If so, we should
1062             // instruct the process monitor to enable/disable DarwinLog support
1063             // now.
1064             Target *target = GetSelectedOrDummyTarget();
1065             if (!target)
1066             {
1067                 // No target, so there is nothing more to do right now.
1068                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
1069                 return true;
1070             }
1071 
1072             // Grab the active process.
1073             auto process_sp = target->GetProcessSP();
1074             if (!process_sp)
1075             {
1076                 // No active process, so there is nothing more to do right
1077                 // now.
1078                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
1079                 return true;
1080             }
1081 
1082             // If the process is no longer alive, we can't do this now.
1083             // We'll catch it the next time the process is started up.
1084             if (!process_sp->IsAlive())
1085             {
1086                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
1087                 return true;
1088             }
1089 
1090             // Get the plugin for the process.
1091             auto plugin_sp =
1092                 process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
1093             if (!plugin_sp ||
1094                 (plugin_sp->GetPluginName() !=
1095                  StructuredDataDarwinLog::GetStaticPluginName()))
1096             {
1097                 result.AppendError("failed to get StructuredDataPlugin for "
1098                                    "the process");
1099                 result.SetStatus(eReturnStatusFailed);
1100             }
1101             StructuredDataDarwinLog &plugin =
1102                 *static_cast<StructuredDataDarwinLog*>(plugin_sp.get());
1103 
1104             if (m_enable)
1105             {
1106                 // To do this next part right, we need to track whether we
1107                 // added the proper environment variable at launch time.
1108                 // It is incorrect to assume that we're enabling after launch,
1109                 // and that therefore if we needed the env var set to properly
1110                 // handle the options, that it is set incorrectly.  The env var
1111                 // could have been added if this is a re-enable using different
1112                 // arguments.  This is a bit tricky as the point where we
1113                 // have to set the env var, we don't yet have a process or the
1114                 // associated darwin-log plugin instance, and thus don't have a
1115                 // great place to stick this knowledge.
1116 #if 0
1117                 // Check if we're attempting to disable debug-level or
1118                 // info-level content but we haven't launched with the magic
1119                 // env var that suppresses the "always add" of those.  In
1120                 // that scenario, the user is asking *not* to see debug or
1121                 // info level messages, but will see them anyway.  Warn and
1122                 // show how to correct it.
1123                 if (!m_options_sp->GetIncludeDebugLevel() &&
1124                     !GetGlobalProperties()->GetUseStrictSources())
1125                 {
1126                     AppendStrictSourcesWarning(result, "debug-level");
1127                 }
1128 
1129                 if (!m_options_sp->GetIncludeInfoLevel() &&
1130                     !GetGlobalProperties()->GetUseStrictSources())
1131                 {
1132                     AppendStrictSourcesWarning(result, "info-level");
1133                 }
1134 #endif
1135 
1136                 // Hook up the breakpoint for the process that detects when
1137                 // libtrace has been sufficiently initialized to really start
1138                 // the os_log stream.  This is insurance to assure us that
1139                 // logging is really enabled.  Requesting that logging be
1140                 // enabled for a process before libtrace is initialized
1141                 // results in a scenario where no errors occur, but no logging
1142                 // is captured, either.  This step is to eliminate that
1143                 // possibility.
1144                 plugin.AddInitCompletionHook(*process_sp.get());
1145             }
1146 
1147             // Send configuration to the feature by way of the process.
1148             // Construct the options we will use.
1149             auto config_sp = m_options_sp->BuildConfigurationData(m_enable);
1150             const Error error =
1151                 process_sp->ConfigureStructuredData(GetDarwinLogTypeName(),
1152                                                     config_sp);
1153 
1154             // Report results.
1155             if (!error.Success())
1156             {
1157                 result.AppendError(error.AsCString());
1158                 result.SetStatus(eReturnStatusFailed);
1159                 // Our configuration failed, so we're definitely disabled.
1160                 plugin.SetEnabled(false);
1161             }
1162             else
1163             {
1164                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
1165                 // Our configuration succeeeded, so we're enabled/disabled
1166                 // per whichever one this command is setup to do.
1167                 plugin.SetEnabled(m_enable);
1168             }
1169             return result.Succeeded();
1170         }
1171 
1172         Options*
1173         GetOptions () override
1174         {
1175             // We don't have options when this represents disable.
1176             return m_enable ? m_options_sp.get() : nullptr;
1177         }
1178 
1179     private:
1180         const bool       m_enable;
1181         EnableOptionsSP  m_options_sp;
1182     };
1183 
1184     // -------------------------------------------------------------------------
1185     /// Provides the status command.
1186     // -------------------------------------------------------------------------
1187     class StatusCommand : public CommandObjectParsed
1188     {
1189     public:
1190 
1191         StatusCommand(CommandInterpreter &interpreter) :
1192         CommandObjectParsed(interpreter, "status",
1193                             "Show whether Darwin log supported is available"
1194                             " and enabled.",
1195                             "plugin structured-data darwin-log status")
1196         {
1197         }
1198 
1199     protected:
1200 
1201         bool
1202         DoExecute (Args& command, CommandReturnObject &result) override
1203         {
1204             auto &stream = result.GetOutputStream();
1205 
1206             // Figure out if we've got a process.  If so, we can tell if
1207             // DarwinLog is available for that process.
1208             Target *target = GetSelectedOrDummyTarget();
1209             auto process_sp = target ? target->GetProcessSP() : ProcessSP();
1210             if (!target || !process_sp)
1211             {
1212                 stream.PutCString("Availability: unknown (requires process)\n");
1213                 stream.PutCString("Enabled: not applicable "
1214                                   "(requires process)\n");
1215             }
1216             else
1217             {
1218                 auto plugin_sp =
1219                     process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
1220                 stream.Printf("Availability: %s\n", plugin_sp ? "available" :
1221                               "unavailable");
1222                 auto &plugin_name =
1223                     StructuredDataDarwinLog::GetStaticPluginName();
1224                 const bool enabled = plugin_sp ?
1225                     plugin_sp->GetEnabled(plugin_name) : false;
1226                 stream.Printf("Enabled: %s\n", enabled ? "true" :
1227                               "false");
1228             }
1229 
1230             // Display filter settings.
1231             DebuggerSP debugger_sp =
1232                 GetCommandInterpreter().GetDebugger().shared_from_this();
1233             auto options_sp = GetGlobalEnableOptions(debugger_sp);
1234             if (!options_sp)
1235             {
1236                 // Nothing more to do.
1237                 result.SetStatus(eReturnStatusSuccessFinishResult);
1238                 return true;
1239             }
1240 
1241             // Print filter rules
1242             stream.PutCString("DarwinLog filter rules:\n");
1243 
1244             stream.IndentMore();
1245 
1246             if (options_sp->GetFilterRules().empty())
1247             {
1248                 stream.Indent();
1249                 stream.PutCString("none\n");
1250             }
1251             else
1252             {
1253                 // Print each of the filter rules.
1254                 int rule_number = 0;
1255                 for (auto rule_sp : options_sp->GetFilterRules())
1256                 {
1257                     ++rule_number;
1258                     if (!rule_sp)
1259                         continue;
1260 
1261                     stream.Indent();
1262                     stream.Printf("%02d: ", rule_number);
1263                     rule_sp->Dump(stream);
1264                     stream.PutChar('\n');
1265                 }
1266             }
1267             stream.IndentLess();
1268 
1269             // Print no-match handling.
1270             stream.Indent();
1271             stream.Printf("no-match behavior: %s\n",
1272                           options_sp->GetFallthroughAccepts() ? "accept" :
1273                           "reject");
1274 
1275             result.SetStatus(eReturnStatusSuccessFinishResult);
1276             return true;
1277         }
1278     };
1279 
1280     // -------------------------------------------------------------------------
1281     /// Provides the darwin-log base command
1282     // -------------------------------------------------------------------------
1283     class BaseCommand : public CommandObjectMultiword
1284     {
1285     public:
1286         BaseCommand(CommandInterpreter &interpreter) :
1287             CommandObjectMultiword(interpreter,
1288                                    "plugin structured-data darwin-log",
1289                                    "Commands for configuring Darwin os_log "
1290                                    "support.",
1291                                    "")
1292         {
1293             // enable
1294             auto enable_help = "Enable Darwin log collection, or re-enable "
1295                                "with modified configuration.";
1296             auto enable_syntax = "plugin structured-data darwin-log enable";
1297             auto enable_cmd_sp =
1298                 CommandObjectSP(new EnableCommand(interpreter,
1299                                                   true, // enable
1300                                                   "enable",
1301                                                   enable_help,
1302                                                   enable_syntax));
1303             LoadSubCommand("enable", enable_cmd_sp);
1304 
1305             // disable
1306             auto disable_help = "Disable Darwin log collection.";
1307             auto disable_syntax = "plugin structured-data darwin-log disable";
1308             auto disable_cmd_sp =
1309                 CommandObjectSP(new EnableCommand(interpreter,
1310                                                   false, // disable
1311                                                   "disable",
1312                                                   disable_help,
1313                                                   disable_syntax));
1314             LoadSubCommand("disable", disable_cmd_sp);
1315 
1316             // status
1317             auto status_cmd_sp =
1318                 CommandObjectSP(new StatusCommand(interpreter));
1319             LoadSubCommand("status", status_cmd_sp);
1320         }
1321     };
1322 
1323     EnableOptionsSP
1324     ParseAutoEnableOptions(Error &error, Debugger &debugger)
1325     {
1326         // We are abusing the options data model here so that we can parse
1327         // options without requiring the Debugger instance.
1328 
1329         // We have an empty execution context at this point.  We only want
1330         // to parse options, and we don't need any context to do this here.
1331         // In fact, we want to be able to parse the enable options before having
1332         // any context.
1333         ExecutionContext exe_ctx;
1334 
1335         EnableOptionsSP options_sp(new EnableOptions());
1336         options_sp->NotifyOptionParsingStarting(&exe_ctx);
1337 
1338         CommandReturnObject result;
1339 
1340         // Parse the arguments.
1341         auto options_property_sp =
1342             debugger.GetPropertyValue(nullptr,
1343                                       "plugin.structured-data.darwin-log."
1344                                       "auto-enable-options",
1345                                       false,
1346                                       error);
1347         if (!error.Success())
1348             return EnableOptionsSP();
1349         if (!options_property_sp)
1350         {
1351             error.SetErrorString("failed to find option setting for "
1352                                  "plugin.structured-data.darwin-log.");
1353             return EnableOptionsSP();
1354         }
1355 
1356         const char *enable_options = options_property_sp->GetAsString()->GetCurrentValue();
1357         Args args(enable_options);
1358         if (args.GetArgumentCount() > 0)
1359         {
1360             // Eliminate the initial '--' that would be required to set the
1361             // settings that themselves include '-' and/or '--'.
1362             const char *first_arg = args.GetArgumentAtIndex(0);
1363             if (first_arg && (strcmp(first_arg, "--") == 0))
1364                 args.Shift();
1365         }
1366 
1367         // ParseOptions calls getopt_long_only, which always skips the zero'th item in the array and starts at position 1,
1368         // so we need to push a dummy value into position zero.
1369         args.Unshift("dummy_string");
1370         bool require_validation = false;
1371         error = args.ParseOptions(*options_sp.get(), &exe_ctx, PlatformSP(),
1372                                   require_validation);
1373         if (!error.Success())
1374             return EnableOptionsSP();
1375 
1376         if (!options_sp->VerifyOptions(result))
1377             return EnableOptionsSP();
1378 
1379         // We successfully parsed and validated the options.
1380         return options_sp;
1381     }
1382 
1383     bool
1384     RunEnableCommand(CommandInterpreter &interpreter)
1385     {
1386         StreamString command_stream;
1387 
1388         command_stream << "plugin structured-data darwin-log enable";
1389         auto enable_options = GetGlobalProperties()->GetAutoEnableOptions();
1390         if (enable_options && (strlen(enable_options) > 0))
1391         {
1392             command_stream << ' ';
1393             command_stream << enable_options;
1394         }
1395 
1396         // Run the command.
1397         CommandReturnObject return_object;
1398         interpreter.HandleCommand(command_stream.GetString().c_str(),
1399                                   eLazyBoolNo,
1400                                   return_object);
1401         return return_object.Succeeded();
1402     }
1403 }
1404 using namespace sddarwinlog_private;
1405 
1406 #pragma mark -
1407 #pragma mark Public static API
1408 
1409 // -----------------------------------------------------------------------------
1410 // Public static API
1411 // -----------------------------------------------------------------------------
1412 
1413 void
1414 StructuredDataDarwinLog::Initialize()
1415 {
1416     RegisterFilterOperations();
1417     PluginManager::RegisterPlugin(GetStaticPluginName(),
1418                                   "Darwin os_log() and os_activity() support",
1419                                   &CreateInstance,
1420                                   &DebuggerInitialize,
1421                                   &FilterLaunchInfo);
1422 }
1423 
1424 void
1425 StructuredDataDarwinLog::Terminate()
1426 {
1427     PluginManager::UnregisterPlugin(&CreateInstance);
1428 }
1429 
1430 const ConstString&
1431 StructuredDataDarwinLog::GetStaticPluginName()
1432 {
1433     static ConstString s_plugin_name("darwin-log");
1434     return s_plugin_name;
1435 }
1436 
1437 #pragma mark -
1438 #pragma mark PluginInterface API
1439 
1440 // -----------------------------------------------------------------------------
1441 // PluginInterface API
1442 // -----------------------------------------------------------------------------
1443 
1444 ConstString
1445 StructuredDataDarwinLog::GetPluginName()
1446 {
1447     return GetStaticPluginName();
1448 }
1449 
1450 uint32_t
1451 StructuredDataDarwinLog::GetPluginVersion()
1452 {
1453     return 1;
1454 }
1455 
1456 #pragma mark -
1457 #pragma mark StructuredDataPlugin API
1458 
1459 // -----------------------------------------------------------------------------
1460 // StructuredDataPlugin API
1461 // -----------------------------------------------------------------------------
1462 
1463 bool
1464 StructuredDataDarwinLog::SupportsStructuredDataType(
1465     const ConstString &type_name)
1466 {
1467     return type_name == GetDarwinLogTypeName();
1468 }
1469 
1470 void
1471 StructuredDataDarwinLog::HandleArrivalOfStructuredData(Process &process,
1472                                                        const ConstString
1473                                                        &type_name,
1474                                                        const
1475                                                        StructuredData::ObjectSP
1476                                                        &object_sp)
1477 {
1478     Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
1479     if (log)
1480     {
1481         StreamString json_stream;
1482         if (object_sp)
1483             object_sp->Dump(json_stream);
1484         else
1485             json_stream.PutCString("<null>");
1486         log->Printf("StructuredDataDarwinLog::%s() called with json: %s",
1487                     __FUNCTION__, json_stream.GetString().c_str());
1488     }
1489 
1490     // Ignore empty structured data.
1491     if (!object_sp)
1492     {
1493         if (log)
1494             log->Printf("StructuredDataDarwinLog::%s() StructuredData object "
1495                         "is null", __FUNCTION__);
1496         return;
1497     }
1498 
1499     // Ignore any data that isn't for us.
1500     if (type_name != GetDarwinLogTypeName())
1501     {
1502         if (log)
1503             log->Printf("StructuredDataDarwinLog::%s() StructuredData type "
1504                         "expected to be %s but was %s, ignoring", __FUNCTION__,
1505                         GetDarwinLogTypeName().AsCString(),
1506                         type_name.AsCString());
1507         return;
1508     }
1509 
1510     // Broadcast the structured data event if we have that enabled.
1511     // This is the way that the outside world (all clients) get access
1512     // to this data.  This plugin sets policy as to whether we do that.
1513     DebuggerSP debugger_sp =
1514         process.GetTarget().GetDebugger().shared_from_this();
1515     auto options_sp = GetGlobalEnableOptions(debugger_sp);
1516     if (options_sp && options_sp->GetBroadcastEvents())
1517     {
1518         if (log)
1519             log->Printf("StructuredDataDarwinLog::%s() broadcasting event",
1520                         __FUNCTION__);
1521         process.BroadcastStructuredData(object_sp, shared_from_this());
1522     }
1523 
1524     // Later, hang on to a configurable amount of these and allow commands
1525     // to inspect, including showing backtraces.
1526 }
1527 
1528 static void
1529 SetErrorWithJSON(Error &error, const char *message,
1530                  StructuredData::Object &object)
1531 {
1532     if (!message)
1533     {
1534         error.SetErrorString("Internal error: message not set.");
1535         return;
1536     }
1537 
1538     StreamString object_stream;
1539     object.Dump(object_stream);
1540     object_stream.Flush();
1541 
1542     error.SetErrorStringWithFormat("%s: %s", message,
1543                                    object_stream.GetString().c_str());
1544 }
1545 
1546 Error
1547 StructuredDataDarwinLog::GetDescription(const StructuredData::ObjectSP
1548                                         &object_sp,
1549                                         lldb_private::Stream &stream)
1550 {
1551     Error error;
1552 
1553     if (!object_sp)
1554     {
1555         error.SetErrorString("No structured data.");
1556         return error;
1557     }
1558 
1559     // Log message payload objects will be dictionaries.
1560     const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
1561     if (!dictionary)
1562     {
1563         SetErrorWithJSON(error, "Structured data should have been a dictionary "
1564                          "but wasn't", *object_sp);
1565         return error;
1566     }
1567 
1568     // Validate this is really a message for our plugin.
1569     ConstString type_name;
1570     if (!dictionary->GetValueForKeyAsString("type", type_name))
1571     {
1572         SetErrorWithJSON(error, "Structured data doesn't contain mandatory "
1573                          "type field", *object_sp);
1574         return error;
1575     }
1576 
1577     if (type_name != GetDarwinLogTypeName())
1578     {
1579         // This is okay - it simply means the data we received is not a log
1580         // message.  We'll just format it as is.
1581         object_sp->Dump(stream);
1582         return error;
1583     }
1584 
1585     // DarwinLog dictionaries store their data
1586     // in an array with key name "events".
1587     StructuredData::Array *events = nullptr;
1588     if (!dictionary->GetValueForKeyAsArray("events", events) ||
1589         !events)
1590     {
1591         SetErrorWithJSON(error, "Log structured data is missing mandatory "
1592                          "'events' field, expected to be an array", *object_sp);
1593         return error;
1594     }
1595 
1596     events->ForEach([&stream, &error, &object_sp, this]
1597                     (StructuredData::Object *object)
1598     {
1599         if (!object)
1600         {
1601             // Invalid.  Stop iterating.
1602             SetErrorWithJSON(error, "Log event entry is null", *object_sp);
1603             return false;
1604         }
1605 
1606         auto event = object->GetAsDictionary();
1607         if (!event)
1608         {
1609             // Invalid, stop iterating.
1610             SetErrorWithJSON(error, "Log event is not a dictionary",
1611                              *object_sp);
1612             return false;
1613         }
1614 
1615         // If we haven't already grabbed the first timestamp
1616         // value, do that now.
1617         if (!m_recorded_first_timestamp)
1618         {
1619             uint64_t timestamp = 0;
1620             if (event->GetValueForKeyAsInteger("timestamp",
1621                                         timestamp))
1622             {
1623                 m_first_timestamp_seen = timestamp;
1624                 m_recorded_first_timestamp = true;
1625             }
1626         }
1627 
1628         HandleDisplayOfEvent(*event, stream);
1629         return true;
1630     });
1631 
1632     stream.Flush();
1633     return error;
1634 }
1635 
1636 bool
1637 StructuredDataDarwinLog::GetEnabled(const ConstString &type_name) const
1638 {
1639     if (type_name == GetStaticPluginName())
1640         return m_is_enabled;
1641     else
1642         return false;
1643 }
1644 
1645 void
1646 StructuredDataDarwinLog::SetEnabled(bool enabled)
1647 {
1648     m_is_enabled = enabled;
1649 }
1650 
1651 void
1652 StructuredDataDarwinLog::ModulesDidLoad(Process &process,
1653                                         ModuleList &module_list)
1654 {
1655     Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
1656     if (log)
1657         log->Printf("StructuredDataDarwinLog::%s called (process uid %u)",
1658                     __FUNCTION__, process.GetUniqueID());
1659 
1660     // Check if we should enable the darwin log support on startup/attach.
1661     if (!GetGlobalProperties()->GetEnableOnStartup() &&
1662         !s_is_explicitly_enabled)
1663     {
1664         // We're neither auto-enabled or explicitly enabled, so we shouldn't
1665         // try to enable here.
1666         if (log)
1667             log->Printf("StructuredDataDarwinLog::%s not applicable, we're not "
1668                         "enabled (process uid %u)", __FUNCTION__,
1669                         process.GetUniqueID());
1670         return;
1671     }
1672 
1673     // If we already added the breakpoint, we've got nothing left to do.
1674     {
1675         std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
1676         if (m_added_breakpoint)
1677         {
1678             if (log)
1679                 log->Printf("StructuredDataDarwinLog::%s process uid %u's "
1680                             "post-libtrace-init breakpoint is already set",
1681                             __FUNCTION__, process.GetUniqueID());
1682             return;
1683         }
1684     }
1685 
1686     // The logging support module name, specifies the name of
1687     // the image name that must be loaded into the debugged process before
1688     // we can try to enable logging.
1689     const char *logging_module_cstr =
1690         GetGlobalProperties()->GetLoggingModuleName();
1691     if (!logging_module_cstr || (logging_module_cstr[0] == 0))
1692     {
1693         // We need this.  Bail.
1694         if (log)
1695             log->Printf("StructuredDataDarwinLog::%s no logging module name "
1696                         "specified, we don't know where to set a breakpoint "
1697                         "(process uid %u)", __FUNCTION__,
1698                         process.GetUniqueID());
1699         return;
1700     }
1701 
1702     const ConstString logging_module_name =
1703         ConstString(logging_module_cstr);
1704 
1705     // We need to see libtrace in the list of modules before we can enable
1706     // tracing for the target process.
1707     bool found_logging_support_module = false;
1708     for (size_t i = 0; i < module_list.GetSize(); ++i)
1709     {
1710         auto module_sp = module_list.GetModuleAtIndex(i);
1711         if (!module_sp)
1712             continue;
1713 
1714         auto &file_spec = module_sp->GetFileSpec();
1715         found_logging_support_module =
1716             (file_spec.GetLastPathComponent() == logging_module_name);
1717         if (found_logging_support_module)
1718             break;
1719     }
1720 
1721     if (!found_logging_support_module)
1722     {
1723         if (log)
1724             log->Printf("StructuredDataDarwinLog::%s logging module %s "
1725                         "has not yet been loaded, can't set a breakpoint "
1726                         "yet (process uid %u)", __FUNCTION__,
1727                         logging_module_name.AsCString(),
1728                         process.GetUniqueID());
1729         return;
1730     }
1731 
1732     // Time to enqueue the breakpoint so we can wait for logging support
1733     // to be initialized before we try to tap the libtrace stream.
1734     AddInitCompletionHook(process);
1735     if (log)
1736         log->Printf("StructuredDataDarwinLog::%s post-init hook breakpoint "
1737                     "set for logging module %s (process uid %u)", __FUNCTION__,
1738                     logging_module_name.AsCString(),
1739                     process.GetUniqueID());
1740 
1741     // We need to try the enable here as well, which will succeed
1742     // in the event that we're attaching to (rather than launching) the
1743     // process and the process is already past initialization time.  In that
1744     // case, the completion breakpoint will never get hit and therefore won't
1745     // start that way.  It doesn't hurt much beyond a bit of bandwidth
1746     // if we end up doing this twice.  It hurts much more if we don't get
1747     // the logging enabled when the user expects it.
1748     EnableNow();
1749 }
1750 
1751 #pragma mark -
1752 #pragma mark Private instance methods
1753 
1754 // -----------------------------------------------------------------------------
1755 // Private constructors
1756 // -----------------------------------------------------------------------------
1757 
1758 StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp) :
1759     StructuredDataPlugin(process_wp),
1760     m_recorded_first_timestamp(false),
1761     m_first_timestamp_seen(0),
1762     m_is_enabled(false),
1763     m_added_breakpoint_mutex(),
1764     m_added_breakpoint()
1765 {
1766 }
1767 
1768 // -----------------------------------------------------------------------------
1769 // Private static methods
1770 // -----------------------------------------------------------------------------
1771 
1772 StructuredDataPluginSP
1773 StructuredDataDarwinLog::CreateInstance(Process &process)
1774 {
1775     // Currently only Apple targets support the os_log/os_activity
1776     // protocol.
1777     if (process.GetTarget().GetArchitecture().GetTriple().getVendor() ==
1778         llvm::Triple::VendorType::Apple)
1779     {
1780         auto process_wp = ProcessWP(process.shared_from_this());
1781         return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp));
1782     }
1783     else
1784     {
1785         return StructuredDataPluginSP();
1786     }
1787 }
1788 
1789 void
1790 StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger)
1791 {
1792     // Setup parent class first.
1793     StructuredDataPlugin::InitializeBasePluginForDebugger(debugger);
1794 
1795     // Get parent command.
1796     auto &interpreter = debugger.GetCommandInterpreter();
1797     std::string parent_command_text = "plugin structured-data";
1798     auto parent_command =
1799         interpreter.GetCommandObjectForCommand(parent_command_text);
1800     if (!parent_command)
1801     {
1802         // Ut oh, parent failed to create parent command.
1803         // TODO log
1804         return;
1805     }
1806 
1807     auto command_name = "darwin-log";
1808     auto command_sp = CommandObjectSP(new BaseCommand(interpreter));
1809     bool result = parent_command->LoadSubCommand(command_name, command_sp);
1810     if (!result)
1811     {
1812         // TODO log it once we setup structured data logging
1813     }
1814 
1815     if (!PluginManager::GetSettingForPlatformPlugin(debugger,
1816                           StructuredDataDarwinLogProperties::GetSettingName()))
1817     {
1818         const bool is_global_setting = true;
1819         PluginManager::CreateSettingForStructuredDataPlugin(debugger,
1820                                     GetGlobalProperties()->GetValueProperties(),
1821                                     ConstString ("Properties for the darwin-log"
1822                                                  " plug-in."),
1823                                     is_global_setting);
1824     }
1825 
1826 }
1827 
1828 Error
1829 StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info,
1830                                           Target *target)
1831 {
1832     Error error;
1833 
1834     // If we're not debugging this launched process, there's nothing for us
1835     // to do here.
1836     if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug))
1837         return error;
1838 
1839     // Darwin os_log() support automatically adds debug-level and info-level
1840     // messages when a debugger is attached to a process.  However, with
1841     // integrated suppport for debugging built into the command-line LLDB,
1842     // the user may specifically set to *not* include debug-level and info-level
1843     // content.  When the user is using the integrated log support, we want
1844     // to put the kabosh on that automatic adding of info and debug level.
1845     // This is done by adding an environment variable to the process on launch.
1846     // (This also means it is not possible to suppress this behavior if
1847     // attaching to an already-running app).
1848     // Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
1849 
1850     // If the target architecture is not one that supports DarwinLog, we have
1851     // nothing to do here.
1852     auto &triple = target ? target->GetArchitecture().GetTriple() :
1853         launch_info.GetArchitecture().GetTriple();
1854     if (triple.getVendor() !=
1855         llvm::Triple::Apple)
1856     {
1857         return error;
1858     }
1859 
1860     // If DarwinLog is not enabled (either by explicit user command or via
1861     // the auto-enable option), then we have nothing to do.
1862     if (!GetGlobalProperties()->GetEnableOnStartup() &&
1863         !s_is_explicitly_enabled)
1864     {
1865         // Nothing to do, DarwinLog is not enabled.
1866         return error;
1867     }
1868 
1869     // If we don't have parsed configuration info, that implies we have
1870     // enable-on-startup set up, but we haven't yet attempted to run the
1871     // enable command.
1872     if (!target)
1873     {
1874         // We really can't do this without a target.  We need to be able
1875         // to get to the debugger to get the proper options to do this right.
1876         // TODO log.
1877         error.SetErrorString("requires a target to auto-enable DarwinLog.");
1878         return error;
1879     }
1880 
1881     DebuggerSP debugger_sp = target->GetDebugger().shared_from_this();
1882     auto options_sp = GetGlobalEnableOptions(debugger_sp);
1883     if (!options_sp && debugger_sp)
1884     {
1885         options_sp = ParseAutoEnableOptions(error, *debugger_sp.get());
1886         if (!options_sp || !error.Success())
1887             return error;
1888 
1889         // We already parsed the options, save them now so we don't generate
1890         // them again until the user runs the command manually.
1891         SetGlobalEnableOptions(debugger_sp, options_sp);
1892     }
1893 
1894     auto &env_vars = launch_info.GetEnvironmentEntries();
1895     if (!options_sp->GetEchoToStdErr())
1896     {
1897         // The user doesn't want to see os_log/NSLog messages echo to stderr.
1898         // That mechanism is entirely separate from the DarwinLog support.
1899         // By default we don't want to get it via stderr, because that would
1900         // be in duplicate of the explicit log support here.
1901 
1902         // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
1903         // echoing of os_log()/NSLog() to stderr in the target program.
1904         size_t argument_index;
1905         if (env_vars.ContainsEnvironmentVariable("OS_ACTIVITY_DT_MODE",
1906                                                  &argument_index))
1907             env_vars.DeleteArgumentAtIndex(argument_index);
1908 
1909         // We will also set the env var that tells any downstream launcher
1910         // from adding OS_ACTIVITY_DT_MODE.
1911         env_vars.AddOrReplaceEnvironmentVariable(
1912                                        "IDE_DISABLED_OS_ACTIVITY_DT_MODE", "1");
1913     }
1914 
1915     // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable
1916     // debug and info level messages.
1917     const char *env_var_value;
1918     if (options_sp->GetIncludeDebugLevel())
1919         env_var_value = "debug";
1920     else if (options_sp->GetIncludeInfoLevel())
1921         env_var_value = "info";
1922     else
1923         env_var_value = "";
1924 
1925     if (env_var_value)
1926     {
1927         const char *env_var_name = "OS_ACTIVITY_MODE";
1928         launch_info.GetEnvironmentEntries()
1929             .AddOrReplaceEnvironmentVariable(env_var_name, env_var_value);
1930     }
1931 
1932     return error;
1933 }
1934 
1935 bool
1936 StructuredDataDarwinLog::InitCompletionHookCallback(void *baton,
1937                                               StoppointCallbackContext *context,
1938                                               lldb::user_id_t break_id,
1939                                               lldb::user_id_t break_loc_id)
1940 {
1941     // We hit the init function.  We now want to enqueue our new thread plan,
1942     // which will in turn enqueue a StepOut thread plan. When the StepOut
1943     // finishes and control returns to our new thread plan, that is the time
1944     // when we can execute our logic to enable the logging support.
1945 
1946     Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
1947     if (log)
1948         log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
1949 
1950     // Get the current thread.
1951     if (!context)
1952     {
1953         if (log)
1954             log->Printf("StructuredDataDarwinLog::%s() warning: no context, "
1955                         "ignoring", __FUNCTION__);
1956         return false;
1957     }
1958 
1959     // Get the plugin from the process.
1960     auto process_sp = context->exe_ctx_ref.GetProcessSP();
1961     if (!process_sp)
1962     {
1963         if (log)
1964             log->Printf("StructuredDataDarwinLog::%s() warning: invalid "
1965                         "process in context, ignoring", __FUNCTION__);
1966         return false;
1967     }
1968     if (log)
1969         log->Printf("StructuredDataDarwinLog::%s() call is for process uid %d",
1970                     __FUNCTION__, process_sp->GetUniqueID());
1971 
1972     auto plugin_sp =
1973         process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
1974     if (!plugin_sp)
1975     {
1976         if (log)
1977             log->Printf("StructuredDataDarwinLog::%s() warning: no plugin for "
1978                         "feature %s in process uid %u",
1979                         __FUNCTION__, GetDarwinLogTypeName().AsCString(),
1980                         process_sp->GetUniqueID());
1981         return false;
1982     }
1983 
1984     // Create the callback for when the thread plan completes.
1985     bool called_enable_method = false;
1986     const auto process_uid = process_sp->GetUniqueID();
1987 
1988     std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp);
1989     ThreadPlanCallOnFunctionExit::Callback callback =
1990         [plugin_wp, &called_enable_method, log, process_uid]() {
1991             if (log)
1992                 log->Printf("StructuredDataDarwinLog::post-init callback: "
1993                             "called (process uid %u)", process_uid);
1994 
1995             auto strong_plugin_sp = plugin_wp.lock();
1996             if (!strong_plugin_sp)
1997             {
1998                 if (log)
1999                     log->Printf("StructuredDataDarwinLog::post-init callback: "
2000                                 "plugin no longer exists, ignoring (process "
2001                                 "uid %u)", process_uid);
2002                 return;
2003             }
2004             // Make sure we only call it once, just in case the
2005             // thread plan hits the breakpoint twice.
2006             if (!called_enable_method)
2007             {
2008                 if (log)
2009                     log->Printf("StructuredDataDarwinLog::post-init callback: "
2010                                 "calling EnableNow() (process uid %u)",
2011                                 process_uid);
2012                 static_cast<StructuredDataDarwinLog*>(strong_plugin_sp.get())
2013                     ->EnableNow();
2014                 called_enable_method = true;
2015             }
2016             else
2017             {
2018                 // Our breakpoint was hit more than once.  Unexpected but
2019                 // no harm done.  Log it.
2020                 if (log)
2021                     log->Printf("StructuredDataDarwinLog::post-init callback: "
2022                                 "skipping EnableNow(), already called by "
2023                                 "callback [we hit this more than once] "
2024                                 "(process uid %u)", process_uid);
2025             }
2026         };
2027 
2028     // Grab the current thread.
2029     auto thread_sp = context->exe_ctx_ref.GetThreadSP();
2030     if (!thread_sp)
2031     {
2032         if (log)
2033             log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
2034                         "retrieve the current thread from the execution "
2035                         "context, nowhere to run the thread plan (process uid "
2036                         "%u)", __FUNCTION__, process_sp->GetUniqueID());
2037         return false;
2038     }
2039 
2040     // Queue the thread plan.
2041     auto thread_plan_sp
2042         = ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp.get(),
2043                                                         callback));
2044     const bool abort_other_plans = false;
2045     thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans);
2046     if (log)
2047         log->Printf("StructuredDataDarwinLog::%s() queuing thread plan on "
2048                     "trace library init method entry (process uid %u)",
2049                     __FUNCTION__, process_sp->GetUniqueID());
2050 
2051     // We return false here to indicate that it isn't a public stop.
2052     return false;
2053 }
2054 
2055 void
2056 StructuredDataDarwinLog::AddInitCompletionHook(Process &process)
2057 {
2058     Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
2059     if (log)
2060         log->Printf("StructuredDataDarwinLog::%s() called (process uid %u)",
2061                     __FUNCTION__, process.GetUniqueID());
2062 
2063     // Make sure we haven't already done this.
2064     {
2065         std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
2066         if (m_added_breakpoint)
2067         {
2068             if (log)
2069                 log->Printf("StructuredDataDarwinLog::%s() ignoring request, "
2070                             "breakpoint already set (process uid %u)",
2071                             __FUNCTION__, process.GetUniqueID());
2072             return;
2073         }
2074 
2075         // We're about to do this, don't let anybody else try to do it.
2076         m_added_breakpoint = true;
2077     }
2078 
2079     // Set a breakpoint for the process that will kick in when libtrace
2080     // has finished its initialization.
2081     Target &target = process.GetTarget();
2082 
2083     // Build up the module list.
2084     FileSpecList module_spec_list;
2085     auto module_file_spec =
2086         FileSpec(GetGlobalProperties()->GetLoggingModuleName(), false);
2087     module_spec_list.Append(module_file_spec);
2088 
2089     // We aren't specifying a source file set.
2090     FileSpecList *source_spec_list = nullptr;
2091 
2092     const char *func_name = "_libtrace_init";
2093     const lldb::addr_t offset = 0;
2094     const LazyBool skip_prologue = eLazyBoolCalculate;
2095     // This is an internal breakpoint - the user shouldn't see it.
2096     const bool internal = true;
2097     const bool hardware = false;
2098 
2099     auto breakpoint_sp =
2100         target.CreateBreakpoint(&module_spec_list, source_spec_list, func_name,
2101                                 eFunctionNameTypeFull, eLanguageTypeC, offset,
2102                                 skip_prologue, internal, hardware);
2103     if (!breakpoint_sp)
2104     {
2105         // Huh?  Bail here.
2106         if (log)
2107             log->Printf("StructuredDataDarwinLog::%s() failed to set "
2108                         "breakpoint in module %s, function %s (process uid %u)",
2109                         __FUNCTION__,
2110                         GetGlobalProperties()->GetLoggingModuleName(),
2111                         func_name, process.GetUniqueID());
2112         return;
2113     }
2114 
2115     // Set our callback.
2116     breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr);
2117     if (log)
2118         log->Printf("StructuredDataDarwinLog::%s() breakpoint set in module %s,"
2119                     "function %s (process uid %u)",
2120                     __FUNCTION__,
2121                     GetGlobalProperties()->GetLoggingModuleName(),
2122                     func_name, process.GetUniqueID());
2123 }
2124 
2125 void
2126 StructuredDataDarwinLog::DumpTimestamp(Stream &stream, uint64_t timestamp)
2127 {
2128     const uint64_t delta_nanos = timestamp - m_first_timestamp_seen;
2129 
2130     const uint64_t hours = delta_nanos / NANOS_PER_HOUR;
2131     uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR;
2132 
2133     const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE;
2134     nanos_remaining = nanos_remaining % NANOS_PER_MINUTE;
2135 
2136     const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND;
2137     nanos_remaining = nanos_remaining % NANOS_PER_SECOND;
2138 
2139     stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64,
2140                   hours, minutes, seconds, nanos_remaining);
2141 }
2142 
2143 size_t
2144 StructuredDataDarwinLog::DumpHeader(Stream &output_stream,
2145                                     const StructuredData::Dictionary &event)
2146 {
2147     StreamString stream;
2148 
2149     ProcessSP process_sp = GetProcess();
2150     if (!process_sp)
2151     {
2152         // TODO log
2153         return 0;
2154     }
2155 
2156     DebuggerSP debugger_sp =
2157         process_sp->GetTarget().GetDebugger().shared_from_this();
2158     if (!debugger_sp)
2159     {
2160         // TODO log
2161         return 0;
2162     }
2163 
2164     auto options_sp = GetGlobalEnableOptions(debugger_sp);
2165     if (!options_sp)
2166     {
2167         // TODO log
2168         return 0;
2169     }
2170 
2171     // Check if we should even display a header.
2172     if (!options_sp->GetDisplayAnyHeaderFields())
2173         return 0;
2174 
2175     stream.PutChar('[');
2176 
2177     int header_count = 0;
2178     if (options_sp->GetDisplayTimestampRelative())
2179     {
2180         uint64_t timestamp = 0;
2181         if (event.GetValueForKeyAsInteger("timestamp", timestamp))
2182         {
2183             DumpTimestamp(stream, timestamp);
2184             ++header_count;
2185         }
2186     }
2187 
2188     if (options_sp->GetDisplayActivityChain())
2189     {
2190         std::string activity_chain;
2191         if (event.GetValueForKeyAsString("activity-chain", activity_chain) &&
2192             !activity_chain.empty())
2193         {
2194             if (header_count > 0)
2195                 stream.PutChar(',');
2196 
2197             // Switch over to the #if 0 branch once we figure out
2198             // how we want to present settings for the tri-state of
2199             // no-activity, activity (most derived only), or activity-chain.
2200 #if 1
2201             // Display the activity chain, from parent-most to child-most
2202             // activity, separated by a colon (:).
2203             stream.PutCString("activity-chain=");
2204             stream.PutCString(activity_chain.c_str());
2205 #else
2206             if (GetGlobalProperties()->GetDisplayActivityChain())
2207             {
2208                 // Display the activity chain, from parent-most to child-most
2209                 // activity, separated by a colon (:).
2210                 stream.PutCString("activity-chain=");
2211                 stream.PutCString(activity_chain.c_str());
2212             }
2213             else
2214             {
2215                 // We're only displaying the child-most activity.
2216                 stream.PutCString("activity=");
2217                 auto pos = activity_chain.find_last_of(':');
2218                 if (pos == std::string::npos)
2219                 {
2220                     // The activity chain only has one level, use the whole
2221                     // thing.
2222                     stream.PutCString(activity_chain.c_str());
2223                 }
2224                 else
2225                 {
2226                     // Display everything after the final ':'.
2227                     stream.PutCString(activity_chain.substr(pos+1).c_str());
2228                 }
2229             }
2230 #endif
2231             ++header_count;
2232         }
2233     }
2234 
2235     if (options_sp->GetDisplaySubsystem())
2236     {
2237         std::string subsystem;
2238         if (event.GetValueForKeyAsString("subsystem",
2239                                          subsystem) &&
2240             !subsystem.empty())
2241         {
2242             if (header_count > 0)
2243                 stream.PutChar(',');
2244             stream.PutCString("subsystem=");
2245             stream.PutCString(subsystem.c_str());
2246             ++header_count;
2247         }
2248     }
2249 
2250     if (options_sp->GetDisplayCategory())
2251     {
2252         std::string category;
2253         if (event.GetValueForKeyAsString("category",
2254                                          category) &&
2255             !category.empty())
2256         {
2257             if (header_count > 0)
2258                 stream.PutChar(',');
2259             stream.PutCString("category=");
2260             stream.PutCString(category.c_str());
2261             ++header_count;
2262         }
2263     }
2264     stream.PutCString("] ");
2265 
2266     auto &result = stream.GetString();
2267     output_stream.PutCString(result.c_str());
2268 
2269     return result.size();
2270 }
2271 
2272 size_t
2273 StructuredDataDarwinLog::HandleDisplayOfEvent(const StructuredData::Dictionary
2274                                               &event, Stream &stream)
2275 {
2276     // Check the type of the event.
2277     ConstString event_type;
2278     if (!event.GetValueForKeyAsString("type", event_type))
2279     {
2280         // Hmm, we expected to get events that describe
2281         // what they are.  Continue anyway.
2282         return 0;
2283     }
2284 
2285     if (event_type != GetLogEventType())
2286         return 0;
2287 
2288     size_t total_bytes = 0;
2289 
2290     // Grab the message content.
2291     std::string message;
2292     if (!event.GetValueForKeyAsString("message", message))
2293         return true;
2294 
2295     // Display the log entry.
2296     const auto len = message.length();
2297 
2298     total_bytes += DumpHeader(stream, event);
2299 
2300     stream.Write(message.c_str(), len);
2301     total_bytes += len;
2302 
2303     // Add an end of line.
2304     stream.PutChar('\n');
2305     total_bytes += sizeof(char);
2306 
2307     return total_bytes;
2308 }
2309 
2310 void
2311 StructuredDataDarwinLog::EnableNow()
2312 {
2313     Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
2314     if (log)
2315         log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
2316 
2317     // Run the enable command.
2318     auto process_sp = GetProcess();
2319     if (!process_sp)
2320     {
2321         // Nothing to do.
2322         if (log)
2323             log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
2324                         "valid process, skipping", __FUNCTION__);
2325         return;
2326     }
2327     if (log)
2328         log->Printf("StructuredDataDarwinLog::%s() call is for process uid %u",
2329                     __FUNCTION__, process_sp->GetUniqueID());
2330 
2331     // If we have configuration data, we can directly enable it now.
2332     // Otherwise, we need to run through the command interpreter to parse
2333     // the auto-run options (which is the only way we get here without having
2334     // already-parsed configuration data).
2335     DebuggerSP debugger_sp =
2336     process_sp->GetTarget().GetDebugger().shared_from_this();
2337     if (!debugger_sp)
2338     {
2339         if (log)
2340             log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
2341                         "debugger shared pointer, skipping (process uid %u)",
2342                         __FUNCTION__, process_sp->GetUniqueID());
2343         return;
2344     }
2345 
2346     auto options_sp = GetGlobalEnableOptions(debugger_sp);
2347     if (!options_sp)
2348     {
2349         // We haven't run the enable command yet.  Just do that now, it'll
2350         // take care of the rest.
2351         auto &interpreter = debugger_sp->GetCommandInterpreter();
2352         const bool success = RunEnableCommand(interpreter);
2353         if (log)
2354         {
2355             if (success)
2356                 log->Printf("StructuredDataDarwinLog::%s() ran enable command "
2357                             "successfully for (process uid %u)",
2358                             __FUNCTION__, process_sp->GetUniqueID());
2359             else
2360                 log->Printf("StructuredDataDarwinLog::%s() error: running "
2361                             "enable command failed (process uid %u)",
2362                             __FUNCTION__, process_sp->GetUniqueID());
2363         }
2364         // Report failures to the debugger error stream.
2365         auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
2366         if (error_stream_sp)
2367         {
2368             error_stream_sp->Printf("failed to configure DarwinLog "
2369                                     "support\n");
2370             error_stream_sp->Flush();
2371         }
2372         return;
2373     }
2374 
2375     // We've previously been enabled. We will re-enable now with the
2376     // previously specified options.
2377     auto config_sp = options_sp->BuildConfigurationData(true);
2378     if (!config_sp)
2379     {
2380         if (log)
2381             log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
2382                         "build configuration data for enable options, skipping "
2383                         "(process uid %u)", __FUNCTION__,
2384                         process_sp->GetUniqueID());
2385         return;
2386     }
2387 
2388     // We can run it directly.
2389     // Send configuration to the feature by way of the process.
2390     const Error error =
2391         process_sp->ConfigureStructuredData(GetDarwinLogTypeName(),
2392                                             config_sp);
2393 
2394     // Report results.
2395     if (!error.Success())
2396     {
2397         if (log)
2398             log->Printf("StructuredDataDarwinLog::%s() "
2399                         "ConfigureStructuredData() call failed "
2400                         "(process uid %u): %s",
2401                         __FUNCTION__, process_sp->GetUniqueID(),
2402                         error.AsCString());
2403         auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
2404         if (error_stream_sp)
2405         {
2406             error_stream_sp->Printf("failed to configure DarwinLog "
2407                                     "support: %s\n", error.AsCString());
2408             error_stream_sp->Flush();
2409         }
2410         m_is_enabled = false;
2411     }
2412     else
2413     {
2414         m_is_enabled = true;
2415         if (log)
2416             log->Printf("StructuredDataDarwinLog::%s() success via direct "
2417                         "configuration (process uid %u)", __FUNCTION__,
2418                         process_sp->GetUniqueID());
2419     }
2420 }
2421