1 //===-- StructuredDataDarwinLog.cpp ---------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "StructuredDataDarwinLog.h"
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/LLDBLog.h"
32 #include "lldb/Utility/Log.h"
33 #include "lldb/Utility/RegularExpression.h"
35 #include "llvm/ADT/StringMap.h"
37 #define DARWIN_LOG_TYPE_VALUE "DarwinLog"
40 using namespace lldb_private
;
42 LLDB_PLUGIN_DEFINE(StructuredDataDarwinLog
)
45 #pragma mark Anonymous Namespace
47 // Anonymous namespace
49 namespace sddarwinlog_private
{
50 const uint64_t NANOS_PER_MICRO
= 1000;
51 const uint64_t NANOS_PER_MILLI
= NANOS_PER_MICRO
* 1000;
52 const uint64_t NANOS_PER_SECOND
= NANOS_PER_MILLI
* 1000;
53 const uint64_t NANOS_PER_MINUTE
= NANOS_PER_SECOND
* 60;
54 const uint64_t NANOS_PER_HOUR
= NANOS_PER_MINUTE
* 60;
56 static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS
= true;
58 /// Global, sticky enable switch. If true, the user has explicitly
59 /// run the enable command. When a process launches or is attached to,
60 /// we will enable DarwinLog if either the settings for auto-enable is
61 /// on, or if the user had explicitly run enable at some point prior
62 /// to the launch/attach.
63 static bool s_is_explicitly_enabled
;
66 using EnableOptionsSP
= std::shared_ptr
<EnableOptions
>;
69 std::map
<DebuggerWP
, EnableOptionsSP
, std::owner_less
<DebuggerWP
>>;
71 static OptionsMap
&GetGlobalOptionsMap() {
72 static OptionsMap s_options_map
;
76 static std::mutex
&GetGlobalOptionsMapLock() {
77 static std::mutex s_options_map_lock
;
78 return s_options_map_lock
;
81 EnableOptionsSP
GetGlobalEnableOptions(const DebuggerSP
&debugger_sp
) {
83 return EnableOptionsSP();
85 std::lock_guard
<std::mutex
> locker(GetGlobalOptionsMapLock());
86 OptionsMap
&options_map
= GetGlobalOptionsMap();
87 DebuggerWP
debugger_wp(debugger_sp
);
88 auto find_it
= options_map
.find(debugger_wp
);
89 if (find_it
!= options_map
.end())
90 return find_it
->second
;
92 return EnableOptionsSP();
95 void SetGlobalEnableOptions(const DebuggerSP
&debugger_sp
,
96 const EnableOptionsSP
&options_sp
) {
97 std::lock_guard
<std::mutex
> locker(GetGlobalOptionsMapLock());
98 OptionsMap
&options_map
= GetGlobalOptionsMap();
99 DebuggerWP
debugger_wp(debugger_sp
);
100 auto find_it
= options_map
.find(debugger_wp
);
101 if (find_it
!= options_map
.end())
102 find_it
->second
= options_sp
;
104 options_map
.insert(std::make_pair(debugger_wp
, options_sp
));
108 #pragma mark Settings Handling
110 /// Code to handle the StructuredDataDarwinLog settings
112 #define LLDB_PROPERTIES_darwinlog
113 #include "StructuredDataDarwinLogProperties.inc"
116 #define LLDB_PROPERTIES_darwinlog
117 #include "StructuredDataDarwinLogPropertiesEnum.inc"
120 class StructuredDataDarwinLogProperties
: public Properties
{
122 static llvm::StringRef
GetSettingName() {
123 static constexpr llvm::StringLiteral
g_setting_name("darwin-log");
124 return g_setting_name
;
127 StructuredDataDarwinLogProperties() : Properties() {
128 m_collection_sp
= std::make_shared
<OptionValueProperties
>(GetSettingName());
129 m_collection_sp
->Initialize(g_darwinlog_properties
);
132 ~StructuredDataDarwinLogProperties() override
= default;
134 bool GetEnableOnStartup() const {
135 const uint32_t idx
= ePropertyEnableOnStartup
;
136 return GetPropertyAtIndexAs
<bool>(
137 idx
, g_darwinlog_properties
[idx
].default_uint_value
!= 0);
140 llvm::StringRef
GetAutoEnableOptions() const {
141 const uint32_t idx
= ePropertyAutoEnableOptions
;
142 return GetPropertyAtIndexAs
<llvm::StringRef
>(
143 idx
, g_darwinlog_properties
[idx
].default_cstr_value
);
146 const char *GetLoggingModuleName() const { return "libsystem_trace.dylib"; }
149 static StructuredDataDarwinLogProperties
&GetGlobalProperties() {
150 static StructuredDataDarwinLogProperties g_settings
;
154 const char *const s_filter_attributes
[] = {
155 "activity", // current activity
156 "activity-chain", // entire activity chain, each level separated by ':'
157 "category", // category of the log message
158 "message", // message contents, fully expanded
159 "subsystem" // subsystem of the log message
161 // Consider implementing this action as it would be cheaper to filter.
162 // "message" requires always formatting the message, which is a waste of
163 // cycles if it ends up being rejected. "format", // format string
164 // used to format message text
167 static llvm::StringRef
GetDarwinLogTypeName() {
168 static constexpr llvm::StringLiteral
s_key_name("DarwinLog");
172 static llvm::StringRef
GetLogEventType() {
173 static constexpr llvm::StringLiteral
s_event_type("log");
178 using FilterRuleSP
= std::shared_ptr
<FilterRule
>;
182 virtual ~FilterRule() = default;
184 using OperationCreationFunc
=
185 std::function
<FilterRuleSP(bool accept
, size_t attribute_index
,
186 const std::string
&op_arg
, Status
&error
)>;
188 static void RegisterOperation(llvm::StringRef operation
,
189 const OperationCreationFunc
&creation_func
) {
190 GetCreationFuncMap().insert(std::make_pair(operation
, creation_func
));
193 static FilterRuleSP
CreateRule(bool match_accepts
, size_t attribute
,
194 llvm::StringRef operation
,
195 const std::string
&op_arg
, Status
&error
) {
196 // Find the creation func for this type of filter rule.
197 auto map
= GetCreationFuncMap();
198 auto find_it
= map
.find(operation
);
199 if (find_it
== map
.end()) {
200 error
.SetErrorStringWithFormatv("unknown filter operation \"{0}\"",
202 return FilterRuleSP();
205 return find_it
->second(match_accepts
, attribute
, op_arg
, error
);
208 StructuredData::ObjectSP
Serialize() const {
209 StructuredData::Dictionary
*dict_p
= new StructuredData::Dictionary();
211 // Indicate whether this is an accept or reject rule.
212 dict_p
->AddBooleanItem("accept", m_accept
);
214 // Indicate which attribute of the message this filter references. This can
215 // drop into the rule-specific DoSerialization if we get to the point where
216 // not all FilterRule derived classes work on an attribute. (e.g. logical
217 // and/or and other compound operations).
218 dict_p
->AddStringItem("attribute", s_filter_attributes
[m_attribute_index
]);
220 // Indicate the type of the rule.
221 dict_p
->AddStringItem("type", GetOperationType());
223 // Let the rule add its own specific details here.
224 DoSerialization(*dict_p
);
226 return StructuredData::ObjectSP(dict_p
);
229 virtual void Dump(Stream
&stream
) const = 0;
231 llvm::StringRef
GetOperationType() const { return m_operation
; }
234 FilterRule(bool accept
, size_t attribute_index
, llvm::StringRef operation
)
235 : m_accept(accept
), m_attribute_index(attribute_index
),
236 m_operation(operation
) {}
238 virtual void DoSerialization(StructuredData::Dictionary
&dict
) const = 0;
240 bool GetMatchAccepts() const { return m_accept
; }
242 const char *GetFilterAttribute() const {
243 return s_filter_attributes
[m_attribute_index
];
247 using CreationFuncMap
= llvm::StringMap
<OperationCreationFunc
>;
249 static CreationFuncMap
&GetCreationFuncMap() {
250 static CreationFuncMap s_map
;
255 const size_t m_attribute_index
;
256 // The lifetime of m_operation should be static.
257 const llvm::StringRef m_operation
;
260 using FilterRules
= std::vector
<FilterRuleSP
>;
262 class RegexFilterRule
: public FilterRule
{
264 static void RegisterOperation() {
265 FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation
);
268 void Dump(Stream
&stream
) const override
{
269 stream
.Printf("%s %s regex %s", GetMatchAccepts() ? "accept" : "reject",
270 GetFilterAttribute(), m_regex_text
.c_str());
274 void DoSerialization(StructuredData::Dictionary
&dict
) const override
{
275 dict
.AddStringItem("regex", m_regex_text
);
279 static FilterRuleSP
CreateOperation(bool accept
, size_t attribute_index
,
280 const std::string
&op_arg
,
282 // We treat the op_arg as a regex. Validate it.
283 if (op_arg
.empty()) {
284 error
.SetErrorString("regex filter type requires a regex "
286 return FilterRuleSP();
289 // Instantiate the regex so we can report any errors.
290 auto regex
= RegularExpression(op_arg
);
291 if (llvm::Error err
= regex
.GetError()) {
292 error
.SetErrorString(llvm::toString(std::move(err
)));
293 return FilterRuleSP();
296 // We passed all our checks, this appears fine.
298 return FilterRuleSP(new RegexFilterRule(accept
, attribute_index
, op_arg
));
301 static llvm::StringRef
StaticGetOperation() {
302 static constexpr llvm::StringLiteral
s_operation("regex");
306 RegexFilterRule(bool accept
, size_t attribute_index
,
307 const std::string
®ex_text
)
308 : FilterRule(accept
, attribute_index
, StaticGetOperation()),
309 m_regex_text(regex_text
) {}
311 const std::string m_regex_text
;
314 class ExactMatchFilterRule
: public FilterRule
{
316 static void RegisterOperation() {
317 FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation
);
320 void Dump(Stream
&stream
) const override
{
321 stream
.Printf("%s %s match %s", GetMatchAccepts() ? "accept" : "reject",
322 GetFilterAttribute(), m_match_text
.c_str());
326 void DoSerialization(StructuredData::Dictionary
&dict
) const override
{
327 dict
.AddStringItem("exact_text", m_match_text
);
331 static FilterRuleSP
CreateOperation(bool accept
, size_t attribute_index
,
332 const std::string
&op_arg
,
334 if (op_arg
.empty()) {
335 error
.SetErrorString("exact match filter type requires an "
336 "argument containing the text that must "
337 "match the specified message attribute.");
338 return FilterRuleSP();
343 new ExactMatchFilterRule(accept
, attribute_index
, op_arg
));
346 static llvm::StringRef
StaticGetOperation() {
347 static constexpr llvm::StringLiteral
s_operation("match");
351 ExactMatchFilterRule(bool accept
, size_t attribute_index
,
352 const std::string
&match_text
)
353 : FilterRule(accept
, attribute_index
, StaticGetOperation()),
354 m_match_text(match_text
) {}
356 const std::string m_match_text
;
359 static void RegisterFilterOperations() {
360 ExactMatchFilterRule::RegisterOperation();
361 RegexFilterRule::RegisterOperation();
364 // =========================================================================
366 // =========================================================================
368 /// Provides the main on-off switch for enabling darwin logging.
370 /// It is valid to run the enable command when logging is already enabled.
371 /// This resets the logging with whatever settings are currently set.
373 static constexpr OptionDefinition g_enable_option_table
[] = {
374 // Source stream include/exclude options (the first-level filter). This one
375 // should be made as small as possible as everything that goes through here
376 // must be processed by the process monitor.
377 {LLDB_OPT_SET_ALL
, false, "any-process", 'a', OptionParser::eNoArgument
,
378 nullptr, {}, 0, eArgTypeNone
,
379 "Specifies log messages from other related processes should be "
381 {LLDB_OPT_SET_ALL
, false, "debug", 'd', OptionParser::eNoArgument
, nullptr,
383 "Specifies debug-level log messages should be included. Specifying"
384 " --debug implies --info."},
385 {LLDB_OPT_SET_ALL
, false, "info", 'i', OptionParser::eNoArgument
, nullptr,
387 "Specifies info-level log messages should be included."},
388 {LLDB_OPT_SET_ALL
, false, "filter", 'f', OptionParser::eRequiredArgument
,
389 nullptr, {}, 0, eArgRawInput
,
390 // There doesn't appear to be a great way for me to have these multi-line,
391 // formatted tables in help. This looks mostly right but there are extra
392 // linefeeds added at seemingly random spots, and indentation isn't
393 // handled properly on those lines.
394 "Appends a filter rule to the log message filter chain. Multiple "
395 "rules may be added by specifying this option multiple times, "
396 "once per filter rule. Filter rules are processed in the order "
397 "they are specified, with the --no-match-accepts setting used "
398 "for any message that doesn't match one of the rules.\n"
400 " Filter spec format:\n"
402 " --filter \"{action} {attribute} {op}\"\n"
409 " activity | // message's most-derived activity\n"
410 " activity-chain | // message's {parent}:{child} activity\n"
411 " category | // message's category\n"
412 " message | // message's expanded contents\n"
413 " subsystem | // message's subsystem\n"
416 " match {exact-match-text} |\n"
417 " regex {search-regex}\n"
419 "The regex flavor used is the C++ std::regex ECMAScript format. "
420 "Prefer character classes like [[:digit:]] to \\d and the like, as "
421 "getting the backslashes escaped through properly is error-prone."},
422 {LLDB_OPT_SET_ALL
, false, "live-stream", 'l',
423 OptionParser::eRequiredArgument
, nullptr, {}, 0, eArgTypeBoolean
,
424 "Specify whether logging events are live-streamed or buffered. "
425 "True indicates live streaming, false indicates buffered. The "
426 "default is true (live streaming). Live streaming will deliver "
427 "log messages with less delay, but buffered capture mode has less "
428 "of an observer effect."},
429 {LLDB_OPT_SET_ALL
, false, "no-match-accepts", 'n',
430 OptionParser::eRequiredArgument
, nullptr, {}, 0, eArgTypeBoolean
,
431 "Specify whether a log message that doesn't match any filter rule "
432 "is accepted or rejected, where true indicates accept. The "
434 {LLDB_OPT_SET_ALL
, false, "echo-to-stderr", 'e',
435 OptionParser::eRequiredArgument
, nullptr, {}, 0, eArgTypeBoolean
,
436 "Specify whether os_log()/NSLog() messages are echoed to the "
437 "target program's stderr. When DarwinLog is enabled, we shut off "
438 "the mirroring of os_log()/NSLog() to the program's stderr. "
439 "Setting this flag to true will restore the stderr mirroring."
440 "The default is false."},
441 {LLDB_OPT_SET_ALL
, false, "broadcast-events", 'b',
442 OptionParser::eRequiredArgument
, nullptr, {}, 0, eArgTypeBoolean
,
443 "Specify if the plugin should broadcast events. Broadcasting "
444 "log events is a requirement for displaying the log entries in "
445 "LLDB command-line. It is also required if LLDB clients want to "
446 "process log events. The default is true."},
447 // Message formatting options
448 {LLDB_OPT_SET_ALL
, false, "timestamp-relative", 'r',
449 OptionParser::eNoArgument
, nullptr, {}, 0, eArgTypeNone
,
450 "Include timestamp in the message header when printing a log "
451 "message. The timestamp is relative to the first displayed "
453 {LLDB_OPT_SET_ALL
, false, "subsystem", 's', OptionParser::eNoArgument
,
454 nullptr, {}, 0, eArgTypeNone
,
455 "Include the subsystem in the message header when displaying "
457 {LLDB_OPT_SET_ALL
, false, "category", 'c', OptionParser::eNoArgument
,
458 nullptr, {}, 0, eArgTypeNone
,
459 "Include the category in the message header when displaying "
461 {LLDB_OPT_SET_ALL
, false, "activity-chain", 'C', OptionParser::eNoArgument
,
462 nullptr, {}, 0, eArgTypeNone
,
463 "Include the activity parent-child chain in the message header "
464 "when displaying a log message. The activity hierarchy is "
465 "displayed as {grandparent-activity}:"
466 "{parent-activity}:{activity}[:...]."},
467 {LLDB_OPT_SET_ALL
, false, "all-fields", 'A', OptionParser::eNoArgument
,
468 nullptr, {}, 0, eArgTypeNone
,
469 "Shortcut to specify that all header fields should be displayed."}};
471 class EnableOptions
: public Options
{
475 m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS
),
478 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
479 m_include_debug_level
= false;
480 m_include_info_level
= false;
481 m_include_any_process
= false;
482 m_filter_fall_through_accepts
= DEFAULT_FILTER_FALLTHROUGH_ACCEPTS
;
483 m_echo_to_stderr
= false;
484 m_display_timestamp_relative
= false;
485 m_display_subsystem
= false;
486 m_display_category
= false;
487 m_display_activity_chain
= false;
488 m_broadcast_events
= true;
489 m_live_stream
= true;
490 m_filter_rules
.clear();
493 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
494 ExecutionContext
*execution_context
) override
{
497 const int short_option
= m_getopt_table
[option_idx
].val
;
498 switch (short_option
) {
500 m_include_any_process
= true;
504 m_display_timestamp_relative
= true;
505 m_display_category
= true;
506 m_display_subsystem
= true;
507 m_display_activity_chain
= true;
512 OptionArgParser::ToBoolean(option_arg
, true, nullptr);
516 m_display_category
= true;
520 m_display_activity_chain
= true;
524 m_include_debug_level
= true;
528 m_echo_to_stderr
= OptionArgParser::ToBoolean(option_arg
, false, nullptr);
532 return ParseFilterRule(option_arg
);
535 m_include_info_level
= true;
539 m_live_stream
= OptionArgParser::ToBoolean(option_arg
, false, nullptr);
543 m_filter_fall_through_accepts
=
544 OptionArgParser::ToBoolean(option_arg
, true, nullptr);
548 m_display_timestamp_relative
= true;
552 m_display_subsystem
= true;
556 error
.SetErrorStringWithFormat("unsupported option '%c'", short_option
);
561 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
562 return llvm::ArrayRef(g_enable_option_table
);
565 StructuredData::DictionarySP
BuildConfigurationData(bool enabled
) {
566 StructuredData::DictionarySP
config_sp(new StructuredData::Dictionary());
568 // Set the basic enabled state.
569 config_sp
->AddBooleanItem("enabled", enabled
);
571 // If we're disabled, there's nothing more to add.
575 // Handle source stream flags.
576 auto source_flags_sp
=
577 StructuredData::DictionarySP(new StructuredData::Dictionary());
578 config_sp
->AddItem("source-flags", source_flags_sp
);
580 source_flags_sp
->AddBooleanItem("any-process", m_include_any_process
);
581 source_flags_sp
->AddBooleanItem("debug-level", m_include_debug_level
);
582 // The debug-level flag, if set, implies info-level.
583 source_flags_sp
->AddBooleanItem("info-level", m_include_info_level
||
584 m_include_debug_level
);
585 source_flags_sp
->AddBooleanItem("live-stream", m_live_stream
);
587 // Specify default filter rule (the fall-through)
588 config_sp
->AddBooleanItem("filter-fall-through-accepts",
589 m_filter_fall_through_accepts
);
591 // Handle filter rules
592 if (!m_filter_rules
.empty()) {
593 auto json_filter_rules_sp
=
594 StructuredData::ArraySP(new StructuredData::Array
);
595 config_sp
->AddItem("filter-rules", json_filter_rules_sp
);
596 for (auto &rule_sp
: m_filter_rules
) {
599 json_filter_rules_sp
->AddItem(rule_sp
->Serialize());
605 bool GetIncludeDebugLevel() const { return m_include_debug_level
; }
607 bool GetIncludeInfoLevel() const {
608 // Specifying debug level implies info level.
609 return m_include_info_level
|| m_include_debug_level
;
612 const FilterRules
&GetFilterRules() const { return m_filter_rules
; }
614 bool GetFallthroughAccepts() const { return m_filter_fall_through_accepts
; }
616 bool GetEchoToStdErr() const { return m_echo_to_stderr
; }
618 bool GetDisplayTimestampRelative() const {
619 return m_display_timestamp_relative
;
622 bool GetDisplaySubsystem() const { return m_display_subsystem
; }
623 bool GetDisplayCategory() const { return m_display_category
; }
624 bool GetDisplayActivityChain() const { return m_display_activity_chain
; }
626 bool GetDisplayAnyHeaderFields() const {
627 return m_display_timestamp_relative
|| m_display_activity_chain
||
628 m_display_subsystem
|| m_display_category
;
631 bool GetBroadcastEvents() const { return m_broadcast_events
; }
634 Status
ParseFilterRule(llvm::StringRef rule_text
) {
637 if (rule_text
.empty()) {
638 error
.SetErrorString("invalid rule_text");
642 // filter spec format:
644 // {action} {attribute} {op}
659 // match {exact-match-text} |
660 // regex {search-regex}
663 auto action_end_pos
= rule_text
.find(' ');
664 if (action_end_pos
== std::string::npos
) {
665 error
.SetErrorStringWithFormat("could not parse filter rule "
666 "action from \"%s\"",
667 rule_text
.str().c_str());
670 auto action
= rule_text
.substr(0, action_end_pos
);
672 if (action
== "accept")
674 else if (action
== "reject")
677 error
.SetErrorString("filter action must be \"accept\" or \"deny\"");
682 auto attribute_end_pos
= rule_text
.find(" ", action_end_pos
+ 1);
683 if (attribute_end_pos
== std::string::npos
) {
684 error
.SetErrorStringWithFormat("could not parse filter rule "
685 "attribute from \"%s\"",
686 rule_text
.str().c_str());
689 auto attribute
= rule_text
.substr(action_end_pos
+ 1,
690 attribute_end_pos
- (action_end_pos
+ 1));
691 auto attribute_index
= MatchAttributeIndex(attribute
);
692 if (attribute_index
< 0) {
693 error
.SetErrorStringWithFormat("filter rule attribute unknown: "
695 attribute
.str().c_str());
700 auto operation_end_pos
= rule_text
.find(" ", attribute_end_pos
+ 1);
701 auto operation
= rule_text
.substr(
702 attribute_end_pos
+ 1, operation_end_pos
- (attribute_end_pos
+ 1));
705 auto rule_sp
= FilterRule::CreateRule(
706 accept
, attribute_index
, operation
,
707 std::string(rule_text
.substr(operation_end_pos
+ 1)), error
);
709 if (rule_sp
&& error
.Success())
710 m_filter_rules
.push_back(rule_sp
);
715 int MatchAttributeIndex(llvm::StringRef attribute_name
) const {
716 for (const auto &Item
: llvm::enumerate(s_filter_attributes
)) {
717 if (attribute_name
== Item
.value())
721 // We didn't match anything.
725 bool m_include_debug_level
= false;
726 bool m_include_info_level
= false;
727 bool m_include_any_process
= false;
728 bool m_filter_fall_through_accepts
;
729 bool m_echo_to_stderr
= false;
730 bool m_display_timestamp_relative
= false;
731 bool m_display_subsystem
= false;
732 bool m_display_category
= false;
733 bool m_display_activity_chain
= false;
734 bool m_broadcast_events
= true;
735 bool m_live_stream
= true;
736 FilterRules m_filter_rules
;
739 class EnableCommand
: public CommandObjectParsed
{
741 EnableCommand(CommandInterpreter
&interpreter
, bool enable
, const char *name
,
742 const char *help
, const char *syntax
)
743 : CommandObjectParsed(interpreter
, name
, help
, syntax
), m_enable(enable
),
744 m_options_sp(enable
? new EnableOptions() : nullptr) {}
747 void AppendStrictSourcesWarning(CommandReturnObject
&result
,
748 const char *source_name
) {
752 // Check if we're *not* using strict sources. If not, then the user is
753 // going to get debug-level info anyways, probably not what they're
754 // expecting. Unfortunately we can only fix this by adding an env var,
755 // which would have had to have happened already. Thus, a warning is the
756 // best we can do here.
758 stream
.Printf("darwin-log source settings specify to exclude "
759 "%s messages, but setting "
760 "'plugin.structured-data.darwin-log."
761 "strict-sources' is disabled. This process will "
762 "automatically have %s messages included. Enable"
763 " the property and relaunch the target binary to have"
764 " these messages excluded.",
765 source_name
, source_name
);
766 result
.AppendWarning(stream
.GetString());
769 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
770 // First off, set the global sticky state of enable/disable based on this
771 // command execution.
772 s_is_explicitly_enabled
= m_enable
;
774 // Next, if this is an enable, save off the option data. We will need it
775 // later if a process hasn't been launched or attached yet.
777 // Save off enabled configuration so we can apply these parsed options
778 // the next time an attach or launch occurs.
779 DebuggerSP debugger_sp
=
780 GetCommandInterpreter().GetDebugger().shared_from_this();
781 SetGlobalEnableOptions(debugger_sp
, m_options_sp
);
784 // Now check if we have a running process. If so, we should instruct the
785 // process monitor to enable/disable DarwinLog support now.
786 Target
&target
= GetSelectedOrDummyTarget();
788 // Grab the active process.
789 auto process_sp
= target
.GetProcessSP();
791 // No active process, so there is nothing more to do right now.
792 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
796 // If the process is no longer alive, we can't do this now. We'll catch it
797 // the next time the process is started up.
798 if (!process_sp
->IsAlive()) {
799 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
803 // Get the plugin for the process.
805 process_sp
->GetStructuredDataPlugin(GetDarwinLogTypeName());
806 if (!plugin_sp
|| (plugin_sp
->GetPluginName() !=
807 StructuredDataDarwinLog::GetStaticPluginName())) {
808 result
.AppendError("failed to get StructuredDataPlugin for "
811 StructuredDataDarwinLog
&plugin
=
812 *static_cast<StructuredDataDarwinLog
*>(plugin_sp
.get());
815 // Hook up the breakpoint for the process that detects when libtrace has
816 // been sufficiently initialized to really start the os_log stream. This
817 // is insurance to assure us that logging is really enabled. Requesting
818 // that logging be enabled for a process before libtrace is initialized
819 // results in a scenario where no errors occur, but no logging is
820 // captured, either. This step is to eliminate that possibility.
821 plugin
.AddInitCompletionHook(*process_sp
);
824 // Send configuration to the feature by way of the process. Construct the
825 // options we will use.
826 auto config_sp
= m_options_sp
->BuildConfigurationData(m_enable
);
828 process_sp
->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp
);
831 if (!error
.Success()) {
832 result
.AppendError(error
.AsCString());
833 // Our configuration failed, so we're definitely disabled.
834 plugin
.SetEnabled(false);
836 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
837 // Our configuration succeeded, so we're enabled/disabled per whichever
838 // one this command is setup to do.
839 plugin
.SetEnabled(m_enable
);
843 Options
*GetOptions() override
{
844 // We don't have options when this represents disable.
845 return m_enable
? m_options_sp
.get() : nullptr;
850 EnableOptionsSP m_options_sp
;
853 /// Provides the status command.
854 class StatusCommand
: public CommandObjectParsed
{
856 StatusCommand(CommandInterpreter
&interpreter
)
857 : CommandObjectParsed(interpreter
, "status",
858 "Show whether Darwin log supported is available"
860 "plugin structured-data darwin-log status") {}
863 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
864 auto &stream
= result
.GetOutputStream();
866 // Figure out if we've got a process. If so, we can tell if DarwinLog is
867 // available for that process.
868 Target
&target
= GetSelectedOrDummyTarget();
869 auto process_sp
= target
.GetProcessSP();
871 stream
.PutCString("Availability: unknown (requires process)\n");
872 stream
.PutCString("Enabled: not applicable "
873 "(requires process)\n");
876 process_sp
->GetStructuredDataPlugin(GetDarwinLogTypeName());
877 stream
.Printf("Availability: %s\n",
878 plugin_sp
? "available" : "unavailable");
880 plugin_sp
? plugin_sp
->GetEnabled(
881 StructuredDataDarwinLog::GetStaticPluginName())
883 stream
.Printf("Enabled: %s\n", enabled
? "true" : "false");
886 // Display filter settings.
887 DebuggerSP debugger_sp
=
888 GetCommandInterpreter().GetDebugger().shared_from_this();
889 auto options_sp
= GetGlobalEnableOptions(debugger_sp
);
891 // Nothing more to do.
892 result
.SetStatus(eReturnStatusSuccessFinishResult
);
896 // Print filter rules
897 stream
.PutCString("DarwinLog filter rules:\n");
901 if (options_sp
->GetFilterRules().empty()) {
903 stream
.PutCString("none\n");
905 // Print each of the filter rules.
907 for (auto rule_sp
: options_sp
->GetFilterRules()) {
913 stream
.Printf("%02d: ", rule_number
);
914 rule_sp
->Dump(stream
);
915 stream
.PutChar('\n');
920 // Print no-match handling.
922 stream
.Printf("no-match behavior: %s\n",
923 options_sp
->GetFallthroughAccepts() ? "accept" : "reject");
925 result
.SetStatus(eReturnStatusSuccessFinishResult
);
929 /// Provides the darwin-log base command
930 class BaseCommand
: public CommandObjectMultiword
{
932 BaseCommand(CommandInterpreter
&interpreter
)
933 : CommandObjectMultiword(interpreter
, "plugin structured-data darwin-log",
934 "Commands for configuring Darwin os_log "
938 auto enable_help
= "Enable Darwin log collection, or re-enable "
939 "with modified configuration.";
940 auto enable_syntax
= "plugin structured-data darwin-log enable";
941 auto enable_cmd_sp
= CommandObjectSP(
942 new EnableCommand(interpreter
,
944 "enable", enable_help
, enable_syntax
));
945 LoadSubCommand("enable", enable_cmd_sp
);
948 auto disable_help
= "Disable Darwin log collection.";
949 auto disable_syntax
= "plugin structured-data darwin-log disable";
950 auto disable_cmd_sp
= CommandObjectSP(
951 new EnableCommand(interpreter
,
953 "disable", disable_help
, disable_syntax
));
954 LoadSubCommand("disable", disable_cmd_sp
);
957 auto status_cmd_sp
= CommandObjectSP(new StatusCommand(interpreter
));
958 LoadSubCommand("status", status_cmd_sp
);
962 EnableOptionsSP
ParseAutoEnableOptions(Status
&error
, Debugger
&debugger
) {
963 Log
*log
= GetLog(LLDBLog::Process
);
964 // We are abusing the options data model here so that we can parse options
965 // without requiring the Debugger instance.
967 // We have an empty execution context at this point. We only want to parse
968 // options, and we don't need any context to do this here. In fact, we want
969 // to be able to parse the enable options before having any context.
970 ExecutionContext exe_ctx
;
972 EnableOptionsSP
options_sp(new EnableOptions());
973 options_sp
->NotifyOptionParsingStarting(&exe_ctx
);
975 CommandReturnObject
result(debugger
.GetUseColor());
977 // Parse the arguments.
978 auto options_property_sp
=
979 debugger
.GetPropertyValue(nullptr,
980 "plugin.structured-data.darwin-log."
981 "auto-enable-options",
983 if (!error
.Success())
984 return EnableOptionsSP();
985 if (!options_property_sp
) {
986 error
.SetErrorString("failed to find option setting for "
987 "plugin.structured-data.darwin-log.");
988 return EnableOptionsSP();
991 const char *enable_options
=
992 options_property_sp
->GetAsString()->GetCurrentValue();
993 Args
args(enable_options
);
994 if (args
.GetArgumentCount() > 0) {
995 // Eliminate the initial '--' that would be required to set the settings
996 // that themselves include '-' and/or '--'.
997 const char *first_arg
= args
.GetArgumentAtIndex(0);
998 if (first_arg
&& (strcmp(first_arg
, "--") == 0))
1002 bool require_validation
= false;
1003 llvm::Expected
<Args
> args_or
=
1004 options_sp
->Parse(args
, &exe_ctx
, PlatformSP(), require_validation
);
1007 log
, args_or
.takeError(),
1008 "Parsing plugin.structured-data.darwin-log.auto-enable-options value "
1010 return EnableOptionsSP();
1013 if (!options_sp
->VerifyOptions(result
))
1014 return EnableOptionsSP();
1016 // We successfully parsed and validated the options.
1020 bool RunEnableCommand(CommandInterpreter
&interpreter
) {
1021 StreamString command_stream
;
1023 command_stream
<< "plugin structured-data darwin-log enable";
1024 auto enable_options
= GetGlobalProperties().GetAutoEnableOptions();
1025 if (!enable_options
.empty()) {
1026 command_stream
<< ' ';
1027 command_stream
<< enable_options
;
1031 CommandReturnObject
return_object(interpreter
.GetDebugger().GetUseColor());
1032 interpreter
.HandleCommand(command_stream
.GetData(), eLazyBoolNo
,
1034 return return_object
.Succeeded();
1037 using namespace sddarwinlog_private
;
1040 #pragma mark Public static API
1042 // Public static API
1044 void StructuredDataDarwinLog::Initialize() {
1045 RegisterFilterOperations();
1046 PluginManager::RegisterPlugin(
1047 GetStaticPluginName(), "Darwin os_log() and os_activity() support",
1048 &CreateInstance
, &DebuggerInitialize
, &FilterLaunchInfo
);
1051 void StructuredDataDarwinLog::Terminate() {
1052 PluginManager::UnregisterPlugin(&CreateInstance
);
1056 #pragma mark StructuredDataPlugin API
1058 // StructuredDataPlugin API
1060 bool StructuredDataDarwinLog::SupportsStructuredDataType(
1061 llvm::StringRef type_name
) {
1062 return type_name
== GetDarwinLogTypeName();
1065 void StructuredDataDarwinLog::HandleArrivalOfStructuredData(
1066 Process
&process
, llvm::StringRef type_name
,
1067 const StructuredData::ObjectSP
&object_sp
) {
1068 Log
*log
= GetLog(LLDBLog::Process
);
1070 StreamString json_stream
;
1072 object_sp
->Dump(json_stream
);
1074 json_stream
.PutCString("<null>");
1075 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() called with json: %s",
1076 __FUNCTION__
, json_stream
.GetData());
1079 // Ignore empty structured data.
1082 "StructuredDataDarwinLog::%s() StructuredData object "
1088 // Ignore any data that isn't for us.
1089 if (type_name
!= GetDarwinLogTypeName()) {
1091 "StructuredData type expected to be {0} but was {1}, ignoring",
1092 GetDarwinLogTypeName(), type_name
);
1096 // Broadcast the structured data event if we have that enabled. This is the
1097 // way that the outside world (all clients) get access to this data. This
1098 // plugin sets policy as to whether we do that.
1099 DebuggerSP debugger_sp
= process
.GetTarget().GetDebugger().shared_from_this();
1100 auto options_sp
= GetGlobalEnableOptions(debugger_sp
);
1101 if (options_sp
&& options_sp
->GetBroadcastEvents()) {
1102 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() broadcasting event",
1104 process
.BroadcastStructuredData(object_sp
, shared_from_this());
1107 // Later, hang on to a configurable amount of these and allow commands to
1108 // inspect, including showing backtraces.
1111 static void SetErrorWithJSON(Status
&error
, const char *message
,
1112 StructuredData::Object
&object
) {
1114 error
.SetErrorString("Internal error: message not set.");
1118 StreamString object_stream
;
1119 object
.Dump(object_stream
);
1120 object_stream
.Flush();
1122 error
.SetErrorStringWithFormat("%s: %s", message
, object_stream
.GetData());
1125 Status
StructuredDataDarwinLog::GetDescription(
1126 const StructuredData::ObjectSP
&object_sp
, lldb_private::Stream
&stream
) {
1130 error
.SetErrorString("No structured data.");
1134 // Log message payload objects will be dictionaries.
1135 const StructuredData::Dictionary
*dictionary
= object_sp
->GetAsDictionary();
1137 SetErrorWithJSON(error
, "Structured data should have been a dictionary "
1143 // Validate this is really a message for our plugin.
1144 llvm::StringRef type_name
;
1145 if (!dictionary
->GetValueForKeyAsString("type", type_name
)) {
1146 SetErrorWithJSON(error
, "Structured data doesn't contain mandatory "
1152 if (type_name
!= GetDarwinLogTypeName()) {
1153 // This is okay - it simply means the data we received is not a log
1154 // message. We'll just format it as is.
1155 object_sp
->Dump(stream
);
1159 // DarwinLog dictionaries store their data
1160 // in an array with key name "events".
1161 StructuredData::Array
*events
= nullptr;
1162 if (!dictionary
->GetValueForKeyAsArray("events", events
) || !events
) {
1163 SetErrorWithJSON(error
, "Log structured data is missing mandatory "
1164 "'events' field, expected to be an array",
1170 [&stream
, &error
, &object_sp
, this](StructuredData::Object
*object
) {
1172 // Invalid. Stop iterating.
1173 SetErrorWithJSON(error
, "Log event entry is null", *object_sp
);
1177 auto event
= object
->GetAsDictionary();
1179 // Invalid, stop iterating.
1180 SetErrorWithJSON(error
, "Log event is not a dictionary", *object_sp
);
1184 // If we haven't already grabbed the first timestamp value, do that
1186 if (!m_recorded_first_timestamp
) {
1187 uint64_t timestamp
= 0;
1188 if (event
->GetValueForKeyAsInteger("timestamp", timestamp
)) {
1189 m_first_timestamp_seen
= timestamp
;
1190 m_recorded_first_timestamp
= true;
1194 HandleDisplayOfEvent(*event
, stream
);
1202 bool StructuredDataDarwinLog::GetEnabled(llvm::StringRef type_name
) const {
1203 if (type_name
== GetStaticPluginName())
1204 return m_is_enabled
;
1208 void StructuredDataDarwinLog::SetEnabled(bool enabled
) {
1209 m_is_enabled
= enabled
;
1212 void StructuredDataDarwinLog::ModulesDidLoad(Process
&process
,
1213 ModuleList
&module_list
) {
1214 Log
*log
= GetLog(LLDBLog::Process
);
1215 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s called (process uid %u)",
1216 __FUNCTION__
, process
.GetUniqueID());
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
1224 "StructuredDataDarwinLog::%s not applicable, we're not "
1225 "enabled (process uid %u)",
1226 __FUNCTION__
, process
.GetUniqueID());
1230 // If we already added the breakpoint, we've got nothing left to do.
1232 std::lock_guard
<std::mutex
> locker(m_added_breakpoint_mutex
);
1233 if (m_added_breakpoint
) {
1235 "StructuredDataDarwinLog::%s process uid %u's "
1236 "post-libtrace-init breakpoint is already set",
1237 __FUNCTION__
, process
.GetUniqueID());
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
1245 const char *logging_module_cstr
=
1246 GetGlobalProperties().GetLoggingModuleName();
1247 if (!logging_module_cstr
|| (logging_module_cstr
[0] == 0)) {
1248 // We need this. Bail.
1250 "StructuredDataDarwinLog::%s no logging module name "
1251 "specified, we don't know where to set a breakpoint "
1253 __FUNCTION__
, process
.GetUniqueID());
1257 // We need to see libtrace in the list of modules before we can enable
1258 // tracing for the target process.
1259 bool found_logging_support_module
= false;
1260 for (size_t i
= 0; i
< module_list
.GetSize(); ++i
) {
1261 auto module_sp
= module_list
.GetModuleAtIndex(i
);
1265 auto &file_spec
= module_sp
->GetFileSpec();
1266 found_logging_support_module
=
1267 (file_spec
.GetFilename() == logging_module_cstr
);
1268 if (found_logging_support_module
)
1272 if (!found_logging_support_module
) {
1274 "StructuredDataDarwinLog::%s logging module %s "
1275 "has not yet been loaded, can't set a breakpoint "
1276 "yet (process uid %u)",
1277 __FUNCTION__
, logging_module_cstr
, process
.GetUniqueID());
1281 // Time to enqueue the breakpoint so we can wait for logging support to be
1282 // initialized before we try to tap the libtrace stream.
1283 AddInitCompletionHook(process
);
1285 "StructuredDataDarwinLog::%s post-init hook breakpoint "
1286 "set for logging module %s (process uid %u)",
1287 __FUNCTION__
, logging_module_cstr
, process
.GetUniqueID());
1289 // We need to try the enable here as well, which will succeed in the event
1290 // that we're attaching to (rather than launching) the process and the
1291 // process is already past initialization time. In that case, the completion
1292 // breakpoint will never get hit and therefore won't start that way. It
1293 // doesn't hurt much beyond a bit of bandwidth if we end up doing this twice.
1294 // It hurts much more if we don't get the logging enabled when the user
1299 // public destructor
1301 StructuredDataDarwinLog::~StructuredDataDarwinLog() {
1302 if (m_breakpoint_id
!= LLDB_INVALID_BREAK_ID
) {
1303 ProcessSP
process_sp(GetProcess());
1305 process_sp
->GetTarget().RemoveBreakpointByID(m_breakpoint_id
);
1306 m_breakpoint_id
= LLDB_INVALID_BREAK_ID
;
1312 #pragma mark Private instance methods
1314 // Private constructors
1316 StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP
&process_wp
)
1317 : StructuredDataPlugin(process_wp
), m_recorded_first_timestamp(false),
1318 m_first_timestamp_seen(0), m_is_enabled(false),
1319 m_added_breakpoint_mutex(), m_added_breakpoint(),
1320 m_breakpoint_id(LLDB_INVALID_BREAK_ID
) {}
1322 // Private static methods
1324 StructuredDataPluginSP
1325 StructuredDataDarwinLog::CreateInstance(Process
&process
) {
1326 // Currently only Apple targets support the os_log/os_activity protocol.
1327 if (process
.GetTarget().GetArchitecture().GetTriple().getVendor() ==
1328 llvm::Triple::VendorType::Apple
) {
1329 auto process_wp
= ProcessWP(process
.shared_from_this());
1330 return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp
));
1332 return StructuredDataPluginSP();
1336 void StructuredDataDarwinLog::DebuggerInitialize(Debugger
&debugger
) {
1337 // Setup parent class first.
1338 StructuredDataPlugin::InitializeBasePluginForDebugger(debugger
);
1340 // Get parent command.
1341 auto &interpreter
= debugger
.GetCommandInterpreter();
1342 llvm::StringRef parent_command_text
= "plugin structured-data";
1343 auto parent_command
=
1344 interpreter
.GetCommandObjectForCommand(parent_command_text
);
1345 if (!parent_command
) {
1346 // Ut oh, parent failed to create parent command.
1351 auto command_name
= "darwin-log";
1352 auto command_sp
= CommandObjectSP(new BaseCommand(interpreter
));
1353 bool result
= parent_command
->LoadSubCommand(command_name
, command_sp
);
1355 // TODO log it once we setup structured data logging
1358 if (!PluginManager::GetSettingForPlatformPlugin(
1359 debugger
, StructuredDataDarwinLogProperties::GetSettingName())) {
1360 const bool is_global_setting
= true;
1361 PluginManager::CreateSettingForStructuredDataPlugin(
1362 debugger
, GetGlobalProperties().GetValueProperties(),
1363 "Properties for the darwin-log plug-in.", is_global_setting
);
1367 Status
StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo
&launch_info
,
1371 // If we're not debugging this launched process, there's nothing for us to do
1373 if (!launch_info
.GetFlags().AnySet(eLaunchFlagDebug
))
1376 // Darwin os_log() support automatically adds debug-level and info-level
1377 // messages when a debugger is attached to a process. However, with
1378 // integrated support for debugging built into the command-line LLDB, the
1379 // user may specifically set to *not* include debug-level and info-level
1380 // content. When the user is using the integrated log support, we want to
1381 // put the kabosh on that automatic adding of info and debug level. This is
1382 // done by adding an environment variable to the process on launch. (This
1383 // also means it is not possible to suppress this behavior if attaching to an
1384 // already-running app).
1385 // Log *log = GetLog(LLDBLog::Platform);
1387 // If the target architecture is not one that supports DarwinLog, we have
1388 // nothing to do here.
1389 auto &triple
= target
? target
->GetArchitecture().GetTriple()
1390 : launch_info
.GetArchitecture().GetTriple();
1391 if (triple
.getVendor() != llvm::Triple::Apple
) {
1395 // If DarwinLog is not enabled (either by explicit user command or via the
1396 // auto-enable option), then we have nothing to do.
1397 if (!GetGlobalProperties().GetEnableOnStartup() &&
1398 !s_is_explicitly_enabled
) {
1399 // Nothing to do, DarwinLog is not enabled.
1403 // If we don't have parsed configuration info, that implies we have enable-
1404 // on-startup set up, but we haven't yet attempted to run the enable command.
1406 // We really can't do this without a target. We need to be able to get to
1407 // the debugger to get the proper options to do this right.
1409 error
.SetErrorString("requires a target to auto-enable DarwinLog.");
1413 DebuggerSP debugger_sp
= target
->GetDebugger().shared_from_this();
1414 auto options_sp
= GetGlobalEnableOptions(debugger_sp
);
1415 if (!options_sp
&& debugger_sp
) {
1416 options_sp
= ParseAutoEnableOptions(error
, *debugger_sp
.get());
1417 if (!options_sp
|| !error
.Success())
1420 // We already parsed the options, save them now so we don't generate them
1421 // again until the user runs the command manually.
1422 SetGlobalEnableOptions(debugger_sp
, options_sp
);
1425 if (!options_sp
->GetEchoToStdErr()) {
1426 // The user doesn't want to see os_log/NSLog messages echo to stderr. That
1427 // mechanism is entirely separate from the DarwinLog support. By default we
1428 // don't want to get it via stderr, because that would be in duplicate of
1429 // the explicit log support here.
1431 // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
1432 // echoing of os_log()/NSLog() to stderr in the target program.
1433 launch_info
.GetEnvironment().erase("OS_ACTIVITY_DT_MODE");
1435 // We will also set the env var that tells any downstream launcher from
1436 // adding OS_ACTIVITY_DT_MODE.
1437 launch_info
.GetEnvironment()["IDE_DISABLED_OS_ACTIVITY_DT_MODE"] = "1";
1440 // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable debug and
1441 // info level messages.
1442 const char *env_var_value
;
1443 if (options_sp
->GetIncludeDebugLevel())
1444 env_var_value
= "debug";
1445 else if (options_sp
->GetIncludeInfoLevel())
1446 env_var_value
= "info";
1448 env_var_value
= "default";
1450 launch_info
.GetEnvironment()["OS_ACTIVITY_MODE"] = env_var_value
;
1455 bool StructuredDataDarwinLog::InitCompletionHookCallback(
1456 void *baton
, StoppointCallbackContext
*context
, lldb::user_id_t break_id
,
1457 lldb::user_id_t break_loc_id
) {
1458 // We hit the init function. We now want to enqueue our new thread plan,
1459 // which will in turn enqueue a StepOut thread plan. When the StepOut
1460 // finishes and control returns to our new thread plan, that is the time when
1461 // we can execute our logic to enable the logging support.
1463 Log
*log
= GetLog(LLDBLog::Process
);
1464 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() called", __FUNCTION__
);
1466 // Get the current thread.
1469 "StructuredDataDarwinLog::%s() warning: no context, "
1475 // Get the plugin from the process.
1476 auto process_sp
= context
->exe_ctx_ref
.GetProcessSP();
1479 "StructuredDataDarwinLog::%s() warning: invalid "
1480 "process in context, ignoring",
1484 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() call is for process uid %d",
1485 __FUNCTION__
, process_sp
->GetUniqueID());
1487 auto plugin_sp
= process_sp
->GetStructuredDataPlugin(GetDarwinLogTypeName());
1489 LLDB_LOG(log
, "warning: no plugin for feature {0} in process uid {1}",
1490 GetDarwinLogTypeName(), process_sp
->GetUniqueID());
1494 // Create the callback for when the thread plan completes.
1495 bool called_enable_method
= false;
1496 const auto process_uid
= process_sp
->GetUniqueID();
1498 std::weak_ptr
<StructuredDataPlugin
> plugin_wp(plugin_sp
);
1499 ThreadPlanCallOnFunctionExit::Callback callback
=
1500 [plugin_wp
, &called_enable_method
, log
, process_uid
]() {
1502 "StructuredDataDarwinLog::post-init callback: "
1503 "called (process uid %u)",
1506 auto strong_plugin_sp
= plugin_wp
.lock();
1507 if (!strong_plugin_sp
) {
1509 "StructuredDataDarwinLog::post-init callback: "
1510 "plugin no longer exists, ignoring (process "
1515 // Make sure we only call it once, just in case the thread plan hits
1516 // the breakpoint twice.
1517 if (!called_enable_method
) {
1519 "StructuredDataDarwinLog::post-init callback: "
1520 "calling EnableNow() (process uid %u)",
1522 static_cast<StructuredDataDarwinLog
*>(strong_plugin_sp
.get())
1524 called_enable_method
= true;
1526 // Our breakpoint was hit more than once. Unexpected but no harm
1529 "StructuredDataDarwinLog::post-init callback: "
1530 "skipping EnableNow(), already called by "
1531 "callback [we hit this more than once] "
1537 // Grab the current thread.
1538 auto thread_sp
= context
->exe_ctx_ref
.GetThreadSP();
1541 "StructuredDataDarwinLog::%s() warning: failed to "
1542 "retrieve the current thread from the execution "
1543 "context, nowhere to run the thread plan (process uid "
1545 __FUNCTION__
, process_sp
->GetUniqueID());
1549 // Queue the thread plan.
1550 auto thread_plan_sp
=
1551 ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp
, callback
));
1552 const bool abort_other_plans
= false;
1553 thread_sp
->QueueThreadPlan(thread_plan_sp
, abort_other_plans
);
1555 "StructuredDataDarwinLog::%s() queuing thread plan on "
1556 "trace library init method entry (process uid %u)",
1557 __FUNCTION__
, process_sp
->GetUniqueID());
1559 // We return false here to indicate that it isn't a public stop.
1563 void StructuredDataDarwinLog::AddInitCompletionHook(Process
&process
) {
1564 Log
*log
= GetLog(LLDBLog::Process
);
1565 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() called (process uid %u)",
1566 __FUNCTION__
, process
.GetUniqueID());
1568 // Make sure we haven't already done this.
1570 std::lock_guard
<std::mutex
> locker(m_added_breakpoint_mutex
);
1571 if (m_added_breakpoint
) {
1573 "StructuredDataDarwinLog::%s() ignoring request, "
1574 "breakpoint already set (process uid %u)",
1575 __FUNCTION__
, process
.GetUniqueID());
1579 // We're about to do this, don't let anybody else try to do it.
1580 m_added_breakpoint
= true;
1583 // Set a breakpoint for the process that will kick in when libtrace has
1584 // finished its initialization.
1585 Target
&target
= process
.GetTarget();
1587 // Build up the module list.
1588 FileSpecList module_spec_list
;
1589 auto module_file_spec
=
1590 FileSpec(GetGlobalProperties().GetLoggingModuleName());
1591 module_spec_list
.Append(module_file_spec
);
1593 // We aren't specifying a source file set.
1594 FileSpecList
*source_spec_list
= nullptr;
1596 const char *func_name
= "_libtrace_init";
1597 const lldb::addr_t offset
= 0;
1598 const LazyBool skip_prologue
= eLazyBoolCalculate
;
1599 // This is an internal breakpoint - the user shouldn't see it.
1600 const bool internal
= true;
1601 const bool hardware
= false;
1603 auto breakpoint_sp
= target
.CreateBreakpoint(
1604 &module_spec_list
, source_spec_list
, func_name
, eFunctionNameTypeFull
,
1605 eLanguageTypeC
, offset
, skip_prologue
, internal
, hardware
);
1606 if (!breakpoint_sp
) {
1609 "StructuredDataDarwinLog::%s() failed to set "
1610 "breakpoint in module %s, function %s (process uid %u)",
1611 __FUNCTION__
, GetGlobalProperties().GetLoggingModuleName(),
1612 func_name
, process
.GetUniqueID());
1616 // Set our callback.
1617 breakpoint_sp
->SetCallback(InitCompletionHookCallback
, nullptr);
1618 m_breakpoint_id
= breakpoint_sp
->GetID();
1620 "StructuredDataDarwinLog::%s() breakpoint set in module %s,"
1621 "function %s (process uid %u)",
1622 __FUNCTION__
, GetGlobalProperties().GetLoggingModuleName(),
1623 func_name
, process
.GetUniqueID());
1626 void StructuredDataDarwinLog::DumpTimestamp(Stream
&stream
,
1627 uint64_t timestamp
) {
1628 const uint64_t delta_nanos
= timestamp
- m_first_timestamp_seen
;
1630 const uint64_t hours
= delta_nanos
/ NANOS_PER_HOUR
;
1631 uint64_t nanos_remaining
= delta_nanos
% NANOS_PER_HOUR
;
1633 const uint64_t minutes
= nanos_remaining
/ NANOS_PER_MINUTE
;
1634 nanos_remaining
= nanos_remaining
% NANOS_PER_MINUTE
;
1636 const uint64_t seconds
= nanos_remaining
/ NANOS_PER_SECOND
;
1637 nanos_remaining
= nanos_remaining
% NANOS_PER_SECOND
;
1639 stream
.Printf("%02" PRIu64
":%02" PRIu64
":%02" PRIu64
".%09" PRIu64
, hours
,
1640 minutes
, seconds
, nanos_remaining
);
1644 StructuredDataDarwinLog::DumpHeader(Stream
&output_stream
,
1645 const StructuredData::Dictionary
&event
) {
1646 StreamString stream
;
1648 ProcessSP process_sp
= GetProcess();
1654 DebuggerSP debugger_sp
=
1655 process_sp
->GetTarget().GetDebugger().shared_from_this();
1661 auto options_sp
= GetGlobalEnableOptions(debugger_sp
);
1667 // Check if we should even display a header.
1668 if (!options_sp
->GetDisplayAnyHeaderFields())
1671 stream
.PutChar('[');
1673 int header_count
= 0;
1674 if (options_sp
->GetDisplayTimestampRelative()) {
1675 uint64_t timestamp
= 0;
1676 if (event
.GetValueForKeyAsInteger("timestamp", timestamp
)) {
1677 DumpTimestamp(stream
, timestamp
);
1682 if (options_sp
->GetDisplayActivityChain()) {
1683 llvm::StringRef activity_chain
;
1684 if (event
.GetValueForKeyAsString("activity-chain", activity_chain
) &&
1685 !activity_chain
.empty()) {
1686 if (header_count
> 0)
1687 stream
.PutChar(',');
1689 // Display the activity chain, from parent-most to child-most activity,
1690 // separated by a colon (:).
1691 stream
.PutCString("activity-chain=");
1692 stream
.PutCString(activity_chain
);
1697 if (options_sp
->GetDisplaySubsystem()) {
1698 llvm::StringRef subsystem
;
1699 if (event
.GetValueForKeyAsString("subsystem", subsystem
) &&
1700 !subsystem
.empty()) {
1701 if (header_count
> 0)
1702 stream
.PutChar(',');
1703 stream
.PutCString("subsystem=");
1704 stream
.PutCString(subsystem
);
1709 if (options_sp
->GetDisplayCategory()) {
1710 llvm::StringRef category
;
1711 if (event
.GetValueForKeyAsString("category", category
) &&
1712 !category
.empty()) {
1713 if (header_count
> 0)
1714 stream
.PutChar(',');
1715 stream
.PutCString("category=");
1716 stream
.PutCString(category
);
1720 stream
.PutCString("] ");
1722 output_stream
.PutCString(stream
.GetString());
1724 return stream
.GetSize();
1727 size_t StructuredDataDarwinLog::HandleDisplayOfEvent(
1728 const StructuredData::Dictionary
&event
, Stream
&stream
) {
1729 // Check the type of the event.
1730 llvm::StringRef event_type
;
1731 if (!event
.GetValueForKeyAsString("type", event_type
)) {
1732 // Hmm, we expected to get events that describe what they are. Continue
1737 if (event_type
!= GetLogEventType())
1740 size_t total_bytes
= 0;
1742 // Grab the message content.
1743 llvm::StringRef message
;
1744 if (!event
.GetValueForKeyAsString("message", message
))
1747 // Display the log entry.
1748 const auto len
= message
.size();
1750 total_bytes
+= DumpHeader(stream
, event
);
1752 stream
.Write(message
.data(), len
);
1755 // Add an end of line.
1756 stream
.PutChar('\n');
1757 total_bytes
+= sizeof(char);
1762 void StructuredDataDarwinLog::EnableNow() {
1763 Log
*log
= GetLog(LLDBLog::Process
);
1764 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() called", __FUNCTION__
);
1766 // Run the enable command.
1767 auto process_sp
= GetProcess();
1771 "StructuredDataDarwinLog::%s() warning: failed to get "
1772 "valid process, skipping",
1776 LLDB_LOGF(log
, "StructuredDataDarwinLog::%s() call is for process uid %u",
1777 __FUNCTION__
, process_sp
->GetUniqueID());
1779 // If we have configuration data, we can directly enable it now. Otherwise,
1780 // we need to run through the command interpreter to parse the auto-run
1781 // options (which is the only way we get here without having already-parsed
1782 // configuration data).
1783 DebuggerSP debugger_sp
=
1784 process_sp
->GetTarget().GetDebugger().shared_from_this();
1787 "StructuredDataDarwinLog::%s() warning: failed to get "
1788 "debugger shared pointer, skipping (process uid %u)",
1789 __FUNCTION__
, process_sp
->GetUniqueID());
1793 auto options_sp
= GetGlobalEnableOptions(debugger_sp
);
1795 // We haven't run the enable command yet. Just do that now, it'll take
1796 // care of the rest.
1797 auto &interpreter
= debugger_sp
->GetCommandInterpreter();
1798 const bool success
= RunEnableCommand(interpreter
);
1802 "StructuredDataDarwinLog::%s() ran enable command "
1803 "successfully for (process uid %u)",
1804 __FUNCTION__
, process_sp
->GetUniqueID());
1807 "StructuredDataDarwinLog::%s() error: running "
1808 "enable command failed (process uid %u)",
1809 __FUNCTION__
, process_sp
->GetUniqueID());
1811 Debugger::ReportError("failed to configure DarwinLog support",
1812 debugger_sp
->GetID());
1816 // We've previously been enabled. We will re-enable now with the previously
1817 // specified options.
1818 auto config_sp
= options_sp
->BuildConfigurationData(true);
1821 "StructuredDataDarwinLog::%s() warning: failed to "
1822 "build configuration data for enable options, skipping "
1824 __FUNCTION__
, process_sp
->GetUniqueID());
1828 // We can run it directly.
1829 // Send configuration to the feature by way of the process.
1830 const Status error
=
1831 process_sp
->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp
);
1834 if (!error
.Success()) {
1836 "StructuredDataDarwinLog::%s() "
1837 "ConfigureStructuredData() call failed "
1838 "(process uid %u): %s",
1839 __FUNCTION__
, process_sp
->GetUniqueID(), error
.AsCString());
1840 Debugger::ReportError("failed to configure DarwinLog support",
1841 debugger_sp
->GetID());
1842 m_is_enabled
= false;
1844 m_is_enabled
= true;
1846 "StructuredDataDarwinLog::%s() success via direct "
1847 "configuration (process uid %u)",
1848 __FUNCTION__
, process_sp
->GetUniqueID());