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