diff options
author | Kate Stone <katherine.stone@apple.com> | 2016-09-06 20:57:50 +0000 |
---|---|---|
committer | Kate Stone <katherine.stone@apple.com> | 2016-09-06 20:57:50 +0000 |
commit | b9c1b51e45b845debb76d8658edabca70ca56079 (patch) | |
tree | dfcb5a13ef2b014202340f47036da383eaee74aa /lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp | |
parent | d5aa73376966339caad04013510626ec2e42c760 (diff) | |
download | llvm-b9c1b51e45b845debb76d8658edabca70ca56079.zip llvm-b9c1b51e45b845debb76d8658edabca70ca56079.tar.gz llvm-b9c1b51e45b845debb76d8658edabca70ca56079.tar.bz2 |
*** This commit represents a complete reformatting of the LLDB source code
*** to conform to clang-format’s LLVM style. This kind of mass change has
*** two obvious implications:
Firstly, merging this particular commit into a downstream fork may be a huge
effort. Alternatively, it may be worth merging all changes up to this commit,
performing the same reformatting operation locally, and then discarding the
merge for this particular commit. The commands used to accomplish this
reformatting were as follows (with current working directory as the root of
the repository):
find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} +
find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ;
The version of clang-format used was 3.9.0, and autopep8 was 1.2.4.
Secondly, “blame” style tools will generally point to this commit instead of
a meaningful prior commit. There are alternatives available that will attempt
to look through this change and find the appropriate prior commit. YMMV.
llvm-svn: 280751
Diffstat (limited to 'lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp')
-rw-r--r-- | lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp | 3863 |
1 files changed, 1728 insertions, 2135 deletions
diff --git a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp index 7e0f080..a7fffdb 100644 --- a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp +++ b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp @@ -43,1076 +43,836 @@ using namespace lldb_private; // Anonymous namespace // ----------------------------------------------------------------------------- -namespace sddarwinlog_private -{ - const uint64_t NANOS_PER_MICRO = 1000; - const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000; - const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; - const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60; - const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60; - - static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true; - - //------------------------------------------------------------------ - /// Global, sticky enable switch. If true, the user has explicitly - /// run the enable command. When a process launches or is attached to, - /// we will enable DarwinLog if either the settings for auto-enable is - /// on, or if the user had explicitly run enable at some point prior - /// to the launch/attach. - //------------------------------------------------------------------ - static bool s_is_explicitly_enabled; - - class EnableOptions; - using EnableOptionsSP = std::shared_ptr<EnableOptions>; - - using OptionsMap = std::map<DebuggerWP, EnableOptionsSP, - std::owner_less<DebuggerWP>>; - - static OptionsMap& - GetGlobalOptionsMap() - { - static OptionsMap s_options_map; - return s_options_map; - } +namespace sddarwinlog_private { +const uint64_t NANOS_PER_MICRO = 1000; +const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000; +const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; +const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60; +const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60; + +static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true; + +//------------------------------------------------------------------ +/// Global, sticky enable switch. If true, the user has explicitly +/// run the enable command. When a process launches or is attached to, +/// we will enable DarwinLog if either the settings for auto-enable is +/// on, or if the user had explicitly run enable at some point prior +/// to the launch/attach. +//------------------------------------------------------------------ +static bool s_is_explicitly_enabled; + +class EnableOptions; +using EnableOptionsSP = std::shared_ptr<EnableOptions>; + +using OptionsMap = + std::map<DebuggerWP, EnableOptionsSP, std::owner_less<DebuggerWP>>; + +static OptionsMap &GetGlobalOptionsMap() { + static OptionsMap s_options_map; + return s_options_map; +} - static std::mutex& - GetGlobalOptionsMapLock() - { - static std::mutex s_options_map_lock; - return s_options_map_lock; - } +static std::mutex &GetGlobalOptionsMapLock() { + static std::mutex s_options_map_lock; + return s_options_map_lock; +} - EnableOptionsSP - GetGlobalEnableOptions(const DebuggerSP &debugger_sp) - { - if (!debugger_sp) - return EnableOptionsSP(); - - std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock()); - OptionsMap &options_map = GetGlobalOptionsMap(); - DebuggerWP debugger_wp(debugger_sp); - auto find_it = options_map.find(debugger_wp); - if (find_it != options_map.end()) - return find_it->second; - else - return EnableOptionsSP(); - } +EnableOptionsSP GetGlobalEnableOptions(const DebuggerSP &debugger_sp) { + if (!debugger_sp) + return EnableOptionsSP(); + + std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock()); + OptionsMap &options_map = GetGlobalOptionsMap(); + DebuggerWP debugger_wp(debugger_sp); + auto find_it = options_map.find(debugger_wp); + if (find_it != options_map.end()) + return find_it->second; + else + return EnableOptionsSP(); +} - void - SetGlobalEnableOptions(const DebuggerSP &debugger_sp, - const EnableOptionsSP &options_sp) - { - std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock()); - OptionsMap &options_map = GetGlobalOptionsMap(); - DebuggerWP debugger_wp(debugger_sp); - auto find_it = options_map.find(debugger_wp); - if (find_it != options_map.end()) - find_it->second = options_sp; - else - options_map.insert(std::make_pair(debugger_wp, options_sp)); - } +void SetGlobalEnableOptions(const DebuggerSP &debugger_sp, + const EnableOptionsSP &options_sp) { + std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock()); + OptionsMap &options_map = GetGlobalOptionsMap(); + DebuggerWP debugger_wp(debugger_sp); + auto find_it = options_map.find(debugger_wp); + if (find_it != options_map.end()) + find_it->second = options_sp; + else + options_map.insert(std::make_pair(debugger_wp, options_sp)); +} #pragma mark - #pragma mark Settings Handling - //------------------------------------------------------------------ - /// Code to handle the StructuredDataDarwinLog settings - //------------------------------------------------------------------ - - static PropertyDefinition - g_properties[] = - { - { - "enable-on-startup" , // name - OptionValue::eTypeBoolean, // type - true, // global - false, // default uint value - nullptr, // default cstring value - nullptr, // enum values - "Enable Darwin os_log collection when debugged process is launched " - "or attached." // description - }, - { - "auto-enable-options" , // name - OptionValue::eTypeString, // type - true, // global - 0, // default uint value - "", // default cstring value - nullptr, // enum values - "Specify the options to 'plugin structured-data darwin-log enable' " - "that should be applied when automatically enabling logging on " - "startup/attach." // description - }, - // Last entry sentinel. - { - nullptr, - OptionValue::eTypeInvalid, - false, - 0 , - nullptr, - nullptr, - nullptr - } - }; - - enum { - ePropertyEnableOnStartup = 0, - ePropertyAutoEnableOptions = 1 - }; - - class StructuredDataDarwinLogProperties : public Properties - { - public: - - static ConstString & - GetSettingName () - { - static ConstString g_setting_name("darwin-log"); - return g_setting_name; - } - - StructuredDataDarwinLogProperties() : - Properties () - { - m_collection_sp.reset(new OptionValueProperties(GetSettingName())); - m_collection_sp->Initialize(g_properties); - } - - virtual - ~StructuredDataDarwinLogProperties() - { - } - - bool - GetEnableOnStartup() const - { - const uint32_t idx = ePropertyEnableOnStartup; - return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, - g_properties[idx].default_uint_value != 0); - } - - const char* - GetAutoEnableOptions() const - { - const uint32_t idx = ePropertyAutoEnableOptions; - return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, - g_properties[idx].default_cstr_value); - } - - const char* - GetLoggingModuleName() const - { - return "libsystem_trace.dylib"; - } - }; - - using StructuredDataDarwinLogPropertiesSP = +//------------------------------------------------------------------ +/// Code to handle the StructuredDataDarwinLog settings +//------------------------------------------------------------------ + +static PropertyDefinition g_properties[] = { + { + "enable-on-startup", // name + OptionValue::eTypeBoolean, // type + true, // global + false, // default uint value + nullptr, // default cstring value + nullptr, // enum values + "Enable Darwin os_log collection when debugged process is launched " + "or attached." // description + }, + { + "auto-enable-options", // name + OptionValue::eTypeString, // type + true, // global + 0, // default uint value + "", // default cstring value + nullptr, // enum values + "Specify the options to 'plugin structured-data darwin-log enable' " + "that should be applied when automatically enabling logging on " + "startup/attach." // description + }, + // Last entry sentinel. + {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; + +enum { ePropertyEnableOnStartup = 0, ePropertyAutoEnableOptions = 1 }; + +class StructuredDataDarwinLogProperties : public Properties { +public: + static ConstString &GetSettingName() { + static ConstString g_setting_name("darwin-log"); + return g_setting_name; + } + + StructuredDataDarwinLogProperties() : Properties() { + m_collection_sp.reset(new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual ~StructuredDataDarwinLogProperties() {} + + bool GetEnableOnStartup() const { + const uint32_t idx = ePropertyEnableOnStartup; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_properties[idx].default_uint_value != 0); + } + + const char *GetAutoEnableOptions() const { + const uint32_t idx = ePropertyAutoEnableOptions; + return m_collection_sp->GetPropertyAtIndexAsString( + nullptr, idx, g_properties[idx].default_cstr_value); + } + + const char *GetLoggingModuleName() const { return "libsystem_trace.dylib"; } +}; + +using StructuredDataDarwinLogPropertiesSP = std::shared_ptr<StructuredDataDarwinLogProperties>; - static const StructuredDataDarwinLogPropertiesSP& - GetGlobalProperties() - { - static StructuredDataDarwinLogPropertiesSP g_settings_sp; - if (!g_settings_sp) - g_settings_sp.reset(new StructuredDataDarwinLogProperties()); - return g_settings_sp; - } - - const char *const s_filter_attributes[] = - { - "activity", // current activity - "activity-chain", // entire activity chain, each level separated by ':' - "category", // category of the log message - "message", // message contents, fully expanded - "subsystem" // subsystem of the log message - - // Consider impelmenting this action as it would be cheaper to filter. - // "message" requires always formatting the message, which is a waste - // of cycles if it ends up being rejected. - // "format", // format string used to format message text - }; - - static const ConstString& - GetDarwinLogTypeName() - { - static const ConstString s_key_name("DarwinLog"); - return s_key_name; - } +static const StructuredDataDarwinLogPropertiesSP &GetGlobalProperties() { + static StructuredDataDarwinLogPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset(new StructuredDataDarwinLogProperties()); + return g_settings_sp; +} - static const ConstString& - GetLogEventType() - { - static const ConstString s_event_type("log"); - return s_event_type; - } +const char *const s_filter_attributes[] = { + "activity", // current activity + "activity-chain", // entire activity chain, each level separated by ':' + "category", // category of the log message + "message", // message contents, fully expanded + "subsystem" // subsystem of the log message + + // Consider impelmenting this action as it would be cheaper to filter. + // "message" requires always formatting the message, which is a waste + // of cycles if it ends up being rejected. + // "format", // format string used to format message text +}; + +static const ConstString &GetDarwinLogTypeName() { + static const ConstString s_key_name("DarwinLog"); + return s_key_name; +} - class FilterRule; - using FilterRuleSP = std::shared_ptr<FilterRule>; +static const ConstString &GetLogEventType() { + static const ConstString s_event_type("log"); + return s_event_type; +} - class FilterRule - { - public: +class FilterRule; +using FilterRuleSP = std::shared_ptr<FilterRule>; + +class FilterRule { +public: + virtual ~FilterRule() {} + + using OperationCreationFunc = + std::function<FilterRuleSP(bool accept, size_t attribute_index, + const std::string &op_arg, Error &error)>; + + static void RegisterOperation(const ConstString &operation, + const OperationCreationFunc &creation_func) { + GetCreationFuncMap().insert(std::make_pair(operation, creation_func)); + } + + static FilterRuleSP CreateRule(bool match_accepts, size_t attribute, + const ConstString &operation, + const std::string &op_arg, Error &error) { + // Find the creation func for this type of filter rule. + auto map = GetCreationFuncMap(); + auto find_it = map.find(operation); + if (find_it == map.end()) { + error.SetErrorStringWithFormat("unknown filter operation \"" + "%s\"", + operation.GetCString()); + return FilterRuleSP(); + } - virtual - ~FilterRule() - { - } + return find_it->second(match_accepts, attribute, op_arg, error); + } - using OperationCreationFunc = - std::function<FilterRuleSP(bool accept, - size_t attribute_index, - const std::string &op_arg, - Error &error)>; - - static void - RegisterOperation(const ConstString &operation, - const OperationCreationFunc &creation_func) - { - GetCreationFuncMap().insert(std::make_pair(operation, - creation_func)); - } + StructuredData::ObjectSP Serialize() const { + StructuredData::Dictionary *dict_p = new StructuredData::Dictionary(); - static - FilterRuleSP CreateRule(bool match_accepts, size_t attribute, - const ConstString &operation, - const std::string &op_arg, Error &error) - { - // Find the creation func for this type of filter rule. - auto map = GetCreationFuncMap(); - auto find_it = map.find(operation); - if (find_it == map.end()) - { - error.SetErrorStringWithFormat("unknown filter operation \"" - "%s\"", - operation.GetCString()); - return FilterRuleSP(); - } - - return find_it->second(match_accepts, attribute, op_arg, error); - } + // Indicate whether this is an accept or reject rule. + dict_p->AddBooleanItem("accept", m_accept); - StructuredData::ObjectSP Serialize() const - { - StructuredData::Dictionary *dict_p = - new StructuredData::Dictionary(); + // Indicate which attribute of the message this filter references. + // This can drop into the rule-specific DoSerialization if we get + // to the point where not all FilterRule derived classes work on + // an attribute. (e.g. logical and/or and other compound + // operations). + dict_p->AddStringItem("attribute", s_filter_attributes[m_attribute_index]); - // Indicate whether this is an accept or reject rule. - dict_p->AddBooleanItem("accept", m_accept); + // Indicate the type of the rule. + dict_p->AddStringItem("type", GetOperationType().GetCString()); - // Indicate which attribute of the message this filter references. - // This can drop into the rule-specific DoSerialization if we get - // to the point where not all FilterRule derived classes work on - // an attribute. (e.g. logical and/or and other compound - // operations). - dict_p->AddStringItem("attribute", - s_filter_attributes[m_attribute_index]); + // Let the rule add its own specific details here. + DoSerialization(*dict_p); - // Indicate the type of the rule. - dict_p->AddStringItem("type", GetOperationType().GetCString()); + return StructuredData::ObjectSP(dict_p); + } - // Let the rule add its own specific details here. - DoSerialization(*dict_p); + virtual void Dump(Stream &stream) const = 0; - return StructuredData::ObjectSP(dict_p); - } + const ConstString &GetOperationType() const { return m_operation; } - virtual void - Dump(Stream &stream) const = 0; +protected: + FilterRule(bool accept, size_t attribute_index, const ConstString &operation) + : m_accept(accept), m_attribute_index(attribute_index), + m_operation(operation) {} - const ConstString& - GetOperationType() const - { - return m_operation; - } + virtual void DoSerialization(StructuredData::Dictionary &dict) const = 0; - protected: + bool GetMatchAccepts() const { return m_accept; } - FilterRule(bool accept, size_t attribute_index, - const ConstString &operation) : - m_accept(accept), - m_attribute_index(attribute_index), - m_operation(operation) - { - } + const char *GetFilterAttribute() const { + return s_filter_attributes[m_attribute_index]; + } - virtual void - DoSerialization(StructuredData::Dictionary &dict) const = 0; +private: + using CreationFuncMap = std::map<ConstString, OperationCreationFunc>; - bool - GetMatchAccepts() const - { - return m_accept; - } + static CreationFuncMap &GetCreationFuncMap() { + static CreationFuncMap s_map; + return s_map; + } - const char* - GetFilterAttribute() const - { - return s_filter_attributes[m_attribute_index]; - } + const bool m_accept; + const size_t m_attribute_index; + const ConstString m_operation; +}; - private: +using FilterRules = std::vector<FilterRuleSP>; - using CreationFuncMap = std::map<ConstString, OperationCreationFunc>; +class RegexFilterRule : public FilterRule { +public: + static void RegisterOperation() { + FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation); + } - static CreationFuncMap& - GetCreationFuncMap() - { - static CreationFuncMap s_map; - return s_map; - } + void Dump(Stream &stream) const override { + stream.Printf("%s %s regex %s", GetMatchAccepts() ? "accept" : "reject", + GetFilterAttribute(), m_regex_text.c_str()); + } - const bool m_accept; - const size_t m_attribute_index; - const ConstString m_operation; +protected: + void DoSerialization(StructuredData::Dictionary &dict) const override { + dict.AddStringItem("regex", m_regex_text); + } - }; +private: + static FilterRuleSP CreateOperation(bool accept, size_t attribute_index, + const std::string &op_arg, Error &error) { + // We treat the op_arg as a regex. Validate it. + if (op_arg.empty()) { + error.SetErrorString("regex filter type requires a regex " + "argument"); + return FilterRuleSP(); + } - using FilterRules = std::vector<FilterRuleSP>; + // Instantiate the regex so we can report any errors. + auto regex = RegularExpression(op_arg.c_str()); + if (!regex.IsValid()) { + char error_text[256]; + error_text[0] = '\0'; + regex.GetErrorAsCString(error_text, sizeof(error_text)); + error.SetErrorString(error_text); + return FilterRuleSP(); + } - class RegexFilterRule : public FilterRule - { - public: + // We passed all our checks, this appears fine. + error.Clear(); + return FilterRuleSP(new RegexFilterRule(accept, attribute_index, op_arg)); + } + + static const ConstString &StaticGetOperation() { + static ConstString s_operation("regex"); + return s_operation; + } + + RegexFilterRule(bool accept, size_t attribute_index, + const std::string ®ex_text) + : FilterRule(accept, attribute_index, StaticGetOperation()), + m_regex_text(regex_text) {} + + const std::string m_regex_text; +}; + +class ExactMatchFilterRule : public FilterRule { +public: + static void RegisterOperation() { + FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation); + } + + void Dump(Stream &stream) const override { + stream.Printf("%s %s match %s", GetMatchAccepts() ? "accept" : "reject", + GetFilterAttribute(), m_match_text.c_str()); + } + +protected: + void DoSerialization(StructuredData::Dictionary &dict) const override { + dict.AddStringItem("exact_text", m_match_text); + } + +private: + static FilterRuleSP CreateOperation(bool accept, size_t attribute_index, + const std::string &op_arg, Error &error) { + if (op_arg.empty()) { + error.SetErrorString("exact match filter type requires an " + "argument containing the text that must " + "match the specified message attribute."); + return FilterRuleSP(); + } - static void - RegisterOperation() - { - FilterRule::RegisterOperation(StaticGetOperation(), - CreateOperation); - } + error.Clear(); + return FilterRuleSP( + new ExactMatchFilterRule(accept, attribute_index, op_arg)); + } - void - Dump(Stream &stream) const override - { - stream.Printf("%s %s regex %s", - GetMatchAccepts() ? "accept" : "reject", - GetFilterAttribute(), m_regex_text.c_str()); - } + static const ConstString &StaticGetOperation() { + static ConstString s_operation("match"); + return s_operation; + } - protected: + ExactMatchFilterRule(bool accept, size_t attribute_index, + const std::string &match_text) + : FilterRule(accept, attribute_index, StaticGetOperation()), + m_match_text(match_text) {} - void - DoSerialization(StructuredData::Dictionary &dict) const override - { - dict.AddStringItem("regex", m_regex_text); - } + const std::string m_match_text; +}; - private: - - static FilterRuleSP - CreateOperation(bool accept, size_t attribute_index, - const std::string &op_arg, Error &error) - { - // We treat the op_arg as a regex. Validate it. - if (op_arg.empty()) - { - error.SetErrorString("regex filter type requires a regex " - "argument"); - return FilterRuleSP(); - } - - // Instantiate the regex so we can report any errors. - auto regex = RegularExpression(op_arg.c_str()); - if (!regex.IsValid()) - { - char error_text[256]; - error_text[0] = '\0'; - regex.GetErrorAsCString(error_text, sizeof(error_text)); - error.SetErrorString(error_text); - return FilterRuleSP(); - } - - // We passed all our checks, this appears fine. - error.Clear(); - return FilterRuleSP(new RegexFilterRule(accept, attribute_index, - op_arg)); - } +static void RegisterFilterOperations() { + ExactMatchFilterRule::RegisterOperation(); + RegexFilterRule::RegisterOperation(); +} - static const ConstString & - StaticGetOperation() - { - static ConstString s_operation("regex"); - return s_operation; - } +// ========================================================================= +// Commands +// ========================================================================= + +// ------------------------------------------------------------------------- +/// Provides the main on-off switch for enabling darwin logging. +/// +/// It is valid to run the enable command when logging is already enabled. +/// This resets the logging with whatever settings are currently set. +// ------------------------------------------------------------------------- +class EnableOptions : public Options { +public: + EnableOptions() + : Options(), m_include_debug_level(false), m_include_info_level(false), + m_include_any_process(false), + m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS), + m_echo_to_stderr(false), m_display_timestamp_relative(false), + m_display_subsystem(false), m_display_category(false), + m_display_activity_chain(false), m_broadcast_events(true), + m_live_stream(true), m_filter_rules() {} + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_include_debug_level = false; + m_include_info_level = false; + m_include_any_process = false; + m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS; + m_echo_to_stderr = false; + m_display_timestamp_relative = false; + m_display_subsystem = false; + m_display_category = false; + m_display_activity_chain = false; + m_broadcast_events = true; + m_live_stream = true; + m_filter_rules.clear(); + } + + Error SetOptionValue(uint32_t option_idx, const char *option_arg, + ExecutionContext *execution_context) override { + Error error; - RegexFilterRule(bool accept, size_t attribute_index, - const std::string ®ex_text) : - FilterRule(accept, attribute_index, StaticGetOperation()), - m_regex_text(regex_text) - { - } + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) { + case 'a': + m_include_any_process = true; + break; + + case 'A': + m_display_timestamp_relative = true; + m_display_category = true; + m_display_subsystem = true; + m_display_activity_chain = true; + break; + + case 'b': + m_broadcast_events = Args::StringToBoolean(option_arg, true, nullptr); + break; + + case 'c': + m_display_category = true; + break; + + case 'C': + m_display_activity_chain = true; + break; + + case 'd': + m_include_debug_level = true; + break; + + case 'e': + m_echo_to_stderr = Args::StringToBoolean(option_arg, false, nullptr); + break; + + case 'f': + return ParseFilterRule(option_arg); + + case 'i': + m_include_info_level = true; + break; + + case 'l': + m_live_stream = Args::StringToBoolean(option_arg, false, nullptr); + break; + + case 'n': + m_filter_fall_through_accepts = + Args::StringToBoolean(option_arg, true, nullptr); + break; + + case 'r': + m_display_timestamp_relative = true; + break; + + case 's': + m_display_subsystem = true; + break; + + default: + error.SetErrorStringWithFormat("unsupported option '%c'", short_option); + } + return error; + } + + const OptionDefinition *GetDefinitions() override { + return g_enable_option_table; + } + + StructuredData::DictionarySP BuildConfigurationData(bool enabled) { + StructuredData::DictionarySP config_sp(new StructuredData::Dictionary()); + + // Set the basic enabled state. + config_sp->AddBooleanItem("enabled", enabled); + + // If we're disabled, there's nothing more to add. + if (!enabled) + return config_sp; + + // Handle source stream flags. + auto source_flags_sp = + StructuredData::DictionarySP(new StructuredData::Dictionary()); + config_sp->AddItem("source-flags", source_flags_sp); + + source_flags_sp->AddBooleanItem("any-process", m_include_any_process); + source_flags_sp->AddBooleanItem("debug-level", m_include_debug_level); + // The debug-level flag, if set, implies info-level. + source_flags_sp->AddBooleanItem("info-level", m_include_info_level || + m_include_debug_level); + source_flags_sp->AddBooleanItem("live-stream", m_live_stream); + + // Specify default filter rule (the fall-through) + config_sp->AddBooleanItem("filter-fall-through-accepts", + m_filter_fall_through_accepts); + + // Handle filter rules + if (!m_filter_rules.empty()) { + auto json_filter_rules_sp = + StructuredData::ArraySP(new StructuredData::Array); + config_sp->AddItem("filter-rules", json_filter_rules_sp); + for (auto &rule_sp : m_filter_rules) { + if (!rule_sp) + continue; + json_filter_rules_sp->AddItem(rule_sp->Serialize()); + } + } + return config_sp; + } - const std::string m_regex_text; - }; + bool GetIncludeDebugLevel() const { return m_include_debug_level; } - class ExactMatchFilterRule : public FilterRule - { - public: + bool GetIncludeInfoLevel() const { + // Specifying debug level implies info level. + return m_include_info_level || m_include_debug_level; + } - static void - RegisterOperation() - { - FilterRule::RegisterOperation(StaticGetOperation(), - CreateOperation); - } + const FilterRules &GetFilterRules() const { return m_filter_rules; } - void - Dump(Stream &stream) const override - { - stream.Printf("%s %s match %s", - GetMatchAccepts() ? "accept" : "reject", - GetFilterAttribute(), m_match_text.c_str()); - } + bool GetFallthroughAccepts() const { return m_filter_fall_through_accepts; } - protected: + bool GetEchoToStdErr() const { return m_echo_to_stderr; } - void - DoSerialization(StructuredData::Dictionary &dict) const override - { - dict.AddStringItem("exact_text", m_match_text); - } + bool GetDisplayTimestampRelative() const { + return m_display_timestamp_relative; + } - private: - - static FilterRuleSP - CreateOperation(bool accept, size_t attribute_index, - const std::string &op_arg, Error &error) - { - if (op_arg.empty()) - { - error.SetErrorString("exact match filter type requires an " - "argument containing the text that must " - "match the specified message attribute."); - return FilterRuleSP(); - } - - error.Clear(); - return FilterRuleSP(new ExactMatchFilterRule(accept, - attribute_index, - op_arg)); - } + bool GetDisplaySubsystem() const { return m_display_subsystem; } + bool GetDisplayCategory() const { return m_display_category; } + bool GetDisplayActivityChain() const { return m_display_activity_chain; } - static const ConstString & - StaticGetOperation() - { - static ConstString s_operation("match"); - return s_operation; - } + bool GetDisplayAnyHeaderFields() const { + return m_display_timestamp_relative || m_display_activity_chain || + m_display_subsystem || m_display_category; + } - ExactMatchFilterRule(bool accept, size_t attribute_index, - const std::string &match_text) : - FilterRule(accept, attribute_index, StaticGetOperation()), - m_match_text(match_text) - { - } + bool GetBroadcastEvents() const { return m_broadcast_events; } - const std::string m_match_text; - }; +private: + Error ParseFilterRule(const char *rule_text_cstr) { + Error error; - static void - RegisterFilterOperations() - { - ExactMatchFilterRule::RegisterOperation(); - RegexFilterRule::RegisterOperation(); + if (!rule_text_cstr || !rule_text_cstr[0]) { + error.SetErrorString("invalid rule_text"); + return error; } - // ========================================================================= - // Commands - // ========================================================================= - - // ------------------------------------------------------------------------- - /// Provides the main on-off switch for enabling darwin logging. - /// - /// It is valid to run the enable command when logging is already enabled. - /// This resets the logging with whatever settings are currently set. - // ------------------------------------------------------------------------- - class EnableOptions : public Options - { - public: - - EnableOptions() : - Options(), - m_include_debug_level(false), - m_include_info_level(false), - m_include_any_process(false), - m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS), - m_echo_to_stderr(false), - m_display_timestamp_relative(false), - m_display_subsystem(false), - m_display_category(false), - m_display_activity_chain(false), - m_broadcast_events(true), - m_live_stream(true), - m_filter_rules() - { - } - - void - OptionParsingStarting (ExecutionContext *execution_context) override - { - m_include_debug_level = false; - m_include_info_level = false; - m_include_any_process = false; - m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS; - m_echo_to_stderr = false; - m_display_timestamp_relative = false; - m_display_subsystem = false; - m_display_category = false; - m_display_activity_chain = false; - m_broadcast_events = true; - m_live_stream = true; - m_filter_rules.clear(); - } - - Error - SetOptionValue (uint32_t option_idx, const char *option_arg, - ExecutionContext *execution_context) override - { - Error error; - - const int short_option = m_getopt_table[option_idx].val; - switch (short_option) - { - case 'a': - m_include_any_process = true; - break; - - case 'A': - m_display_timestamp_relative = true; - m_display_category = true; - m_display_subsystem = true; - m_display_activity_chain = true; - break; - - case 'b': - m_broadcast_events = - Args::StringToBoolean(option_arg, true, nullptr); - break; - - case 'c': - m_display_category = true; - break; - - case 'C': - m_display_activity_chain = true; - break; - - case 'd': - m_include_debug_level = true; - break; - - case 'e': - m_echo_to_stderr = - Args::StringToBoolean(option_arg, false, nullptr); - break; - - case 'f': - return ParseFilterRule(option_arg); - - case 'i': - m_include_info_level = true; - break; - - case 'l': - m_live_stream = - Args::StringToBoolean(option_arg, false, nullptr); - break; - - case 'n': - m_filter_fall_through_accepts = - Args::StringToBoolean(option_arg, true, nullptr); - break; - - case 'r': - m_display_timestamp_relative = true; - break; - - case 's': - m_display_subsystem = true; - break; - - default: - error.SetErrorStringWithFormat("unsupported option '%c'", - short_option); - } - return error; - } - - const OptionDefinition* - GetDefinitions () override - { - return g_enable_option_table; - } - - StructuredData::DictionarySP - BuildConfigurationData(bool enabled) - { - StructuredData::DictionarySP config_sp(new StructuredData:: - Dictionary()); - - // Set the basic enabled state. - config_sp->AddBooleanItem("enabled", enabled); - - // If we're disabled, there's nothing more to add. - if (!enabled) - return config_sp; - - // Handle source stream flags. - auto source_flags_sp = - StructuredData::DictionarySP(new StructuredData::Dictionary()); - config_sp->AddItem("source-flags", source_flags_sp); - - source_flags_sp->AddBooleanItem("any-process", - m_include_any_process); - source_flags_sp->AddBooleanItem("debug-level", - m_include_debug_level); - // The debug-level flag, if set, implies info-level. - source_flags_sp->AddBooleanItem("info-level", - m_include_info_level || - m_include_debug_level); - source_flags_sp->AddBooleanItem("live-stream", - m_live_stream); - - // Specify default filter rule (the fall-through) - config_sp->AddBooleanItem("filter-fall-through-accepts", - m_filter_fall_through_accepts); - - - // Handle filter rules - if (!m_filter_rules.empty()) - { - auto json_filter_rules_sp = - StructuredData::ArraySP(new StructuredData::Array); - config_sp->AddItem("filter-rules", - json_filter_rules_sp); - for (auto &rule_sp : m_filter_rules) - { - if (!rule_sp) - continue; - json_filter_rules_sp->AddItem(rule_sp->Serialize()); - } - } - return config_sp; - } - - bool - GetIncludeDebugLevel() const - { - return m_include_debug_level; - } - - bool - GetIncludeInfoLevel() const - { - // Specifying debug level implies info level. - return m_include_info_level || m_include_debug_level; - } + // filter spec format: + // + // {action} {attribute} {op} + // + // {action} := + // accept | + // reject + // + // {attribute} := + // category | + // subsystem | + // activity | + // activity-chain | + // message | + // format + // + // {op} := + // match {exact-match-text} | + // regex {search-regex} + + const std::string rule_text(rule_text_cstr); + + // Parse action. + auto action_end_pos = rule_text.find(" "); + if (action_end_pos == std::string::npos) { + error.SetErrorStringWithFormat("could not parse filter rule " + "action from \"%s\"", + rule_text_cstr); + return error; + } + auto action = rule_text.substr(0, action_end_pos); + bool accept; + if (action == "accept") + accept = true; + else if (action == "reject") + accept = false; + else { + error.SetErrorString("filter action must be \"accept\" or " + "\"deny\""); + return error; + } - const FilterRules& - GetFilterRules() const - { - return m_filter_rules; - } + // parse attribute + auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1); + if (attribute_end_pos == std::string::npos) { + error.SetErrorStringWithFormat("could not parse filter rule " + "attribute from \"%s\"", + rule_text_cstr); + return error; + } + auto attribute = rule_text.substr(action_end_pos + 1, + attribute_end_pos - (action_end_pos + 1)); + auto attribute_index = MatchAttributeIndex(attribute); + if (attribute_index < 0) { + error.SetErrorStringWithFormat("filter rule attribute unknown: " + "%s", + attribute.c_str()); + return error; + } - bool - GetFallthroughAccepts() const - { - return m_filter_fall_through_accepts; - } + // parse operation + auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1); + auto operation = rule_text.substr( + attribute_end_pos + 1, operation_end_pos - (attribute_end_pos + 1)); - bool - GetEchoToStdErr() const - { - return m_echo_to_stderr; - } + // add filter spec + auto rule_sp = + FilterRule::CreateRule(accept, attribute_index, ConstString(operation), + rule_text.substr(operation_end_pos + 1), error); - bool - GetDisplayTimestampRelative() const - { - return m_display_timestamp_relative; - } + if (rule_sp && error.Success()) + m_filter_rules.push_back(rule_sp); - bool - GetDisplaySubsystem() const - { - return m_display_subsystem; - } - bool - GetDisplayCategory() const - { - return m_display_category; - } - bool - GetDisplayActivityChain() const - { - return m_display_activity_chain; - } - - bool - GetDisplayAnyHeaderFields() const - { - return - m_display_timestamp_relative || - m_display_activity_chain || - m_display_subsystem || - m_display_category; - } - - bool - GetBroadcastEvents() const - { - return m_broadcast_events; - } + return error; + } + + int MatchAttributeIndex(const std::string &attribute_name) { + auto attribute_count = + sizeof(s_filter_attributes) / sizeof(s_filter_attributes[0]); + for (size_t i = 0; i < attribute_count; ++i) { + if (attribute_name == s_filter_attributes[i]) + return static_cast<int>(i); + } - private: - - Error - ParseFilterRule(const char *rule_text_cstr) - { - Error error; - - if (!rule_text_cstr || !rule_text_cstr[0]) - { - error.SetErrorString("invalid rule_text"); - return error; - } - - // filter spec format: - // - // {action} {attribute} {op} - // - // {action} := - // accept | - // reject - // - // {attribute} := - // category | - // subsystem | - // activity | - // activity-chain | - // message | - // format - // - // {op} := - // match {exact-match-text} | - // regex {search-regex} - - const std::string rule_text(rule_text_cstr); - - // Parse action. - auto action_end_pos = rule_text.find(" "); - if (action_end_pos == std::string::npos) - { - error.SetErrorStringWithFormat("could not parse filter rule " - "action from \"%s\"", - rule_text_cstr); - return error; - } - auto action = rule_text.substr(0, action_end_pos); - bool accept; - if (action == "accept") - accept = true; - else if (action == "reject") - accept = false; - else - { - error.SetErrorString("filter action must be \"accept\" or " - "\"deny\""); - return error; - } - - // parse attribute - auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1); - if (attribute_end_pos == std::string::npos) - { - error.SetErrorStringWithFormat("could not parse filter rule " - "attribute from \"%s\"", - rule_text_cstr); - return error; - } - auto attribute = - rule_text.substr(action_end_pos + 1, - attribute_end_pos - (action_end_pos + 1)); - auto attribute_index = MatchAttributeIndex(attribute); - if (attribute_index < 0) - { - error.SetErrorStringWithFormat("filter rule attribute unknown: " - "%s", attribute.c_str()); - return error; - } - - // parse operation - auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1); - auto operation = - rule_text.substr(attribute_end_pos + 1, - operation_end_pos - (attribute_end_pos + 1)); - - // add filter spec - auto rule_sp = - FilterRule::CreateRule(accept, attribute_index, - ConstString(operation), - rule_text.substr(operation_end_pos + 1), - error); - - if (rule_sp && error.Success()) - m_filter_rules.push_back(rule_sp); - - return error; - } + // We didn't match anything. + return -1; + } + + static OptionDefinition g_enable_option_table[]; + + bool m_include_debug_level; + bool m_include_info_level; + bool m_include_any_process; + bool m_filter_fall_through_accepts; + bool m_echo_to_stderr; + bool m_display_timestamp_relative; + bool m_display_subsystem; + bool m_display_category; + bool m_display_activity_chain; + bool m_broadcast_events; + bool m_live_stream; + FilterRules m_filter_rules; +}; + +OptionDefinition EnableOptions::g_enable_option_table[] = { + // Source stream include/exclude options (the first-level filter). + // This one should be made as small as possible as everything that + // goes through here must be processed by the process monitor. + {LLDB_OPT_SET_ALL, false, "any-process", 'a', OptionParser::eNoArgument, + nullptr, nullptr, 0, eArgTypeNone, + "Specifies log messages from other related processes should be " + "included."}, + {LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument, nullptr, + nullptr, 0, eArgTypeNone, + "Specifies debug-level log messages should be included. Specifying" + " --debug implies --info."}, + {LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument, nullptr, + nullptr, 0, eArgTypeNone, + "Specifies info-level log messages should be included."}, + {LLDB_OPT_SET_ALL, false, "filter", 'f', OptionParser::eRequiredArgument, + nullptr, nullptr, 0, eArgRawInput, + // There doesn't appear to be a great way for me to have these + // multi-line, formatted tables in help. This looks mostly right + // but there are extra linefeeds added at seemingly random spots, + // and indentation isn't handled properly on those lines. + "Appends a filter rule to the log message filter chain. Multiple " + "rules may be added by specifying this option multiple times, " + "once per filter rule. Filter rules are processed in the order " + "they are specified, with the --no-match-accepts setting used " + "for any message that doesn't match one of the rules.\n" + "\n" + " Filter spec format:\n" + "\n" + " --filter \"{action} {attribute} {op}\"\n" + "\n" + " {action} :=\n" + " accept |\n" + " reject\n" + "\n" + " {attribute} :=\n" + " activity | // message's most-derived activity\n" + " activity-chain | // message's {parent}:{child} activity\n" + " category | // message's category\n" + " message | // message's expanded contents\n" + " subsystem | // message's subsystem\n" + "\n" + " {op} :=\n" + " match {exact-match-text} |\n" + " regex {search-regex}\n" + "\n" + "The regex flavor used is the C++ std::regex ECMAScript format. " + "Prefer character classes like [[:digit:]] to \\d and the like, as " + "getting the backslashes escaped through properly is error-prone."}, + {LLDB_OPT_SET_ALL, false, "live-stream", 'l', + OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + "Specify whether logging events are live-streamed or buffered. " + "True indicates live streaming, false indicates buffered. The " + "default is true (live streaming). Live streaming will deliver " + "log messages with less delay, but buffered capture mode has less " + "of an observer effect."}, + {LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n', + OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + "Specify whether a log message that doesn't match any filter rule " + "is accepted or rejected, where true indicates accept. The " + "default is true."}, + {LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e', + OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + "Specify whether os_log()/NSLog() messages are echoed to the " + "target program's stderr. When DarwinLog is enabled, we shut off " + "the mirroring of os_log()/NSLog() to the program's stderr. " + "Setting this flag to true will restore the stderr mirroring." + "The default is false."}, + {LLDB_OPT_SET_ALL, false, "broadcast-events", 'b', + OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + "Specify if the plugin should broadcast events. Broadcasting " + "log events is a requirement for displaying the log entries in " + "LLDB command-line. It is also required if LLDB clients want to " + "process log events. The default is true."}, + // Message formatting options + {LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r', + OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, + "Include timestamp in the message header when printing a log " + "message. The timestamp is relative to the first displayed " + "message."}, + {LLDB_OPT_SET_ALL, false, "subsystem", 's', OptionParser::eNoArgument, + nullptr, nullptr, 0, eArgTypeNone, + "Include the subsystem in the the message header when displaying " + "a log message."}, + {LLDB_OPT_SET_ALL, false, "category", 'c', OptionParser::eNoArgument, + nullptr, nullptr, 0, eArgTypeNone, + "Include the category in the the message header when displaying " + "a log message."}, + {LLDB_OPT_SET_ALL, false, "activity-chain", 'C', OptionParser::eNoArgument, + nullptr, nullptr, 0, eArgTypeNone, + "Include the activity parent-child chain in the the message header " + "when displaying a log message. The activity hierarchy is " + "displayed as {grandparent-activity}:" + "{parent-activity}:{activity}[:...]."}, + {LLDB_OPT_SET_ALL, false, "all-fields", 'A', OptionParser::eNoArgument, + nullptr, nullptr, 0, eArgTypeNone, + "Shortcut to specify that all header fields should be displayed."}, + + // Tail sentinel entry + {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}}; + +class EnableCommand : public CommandObjectParsed { +public: + EnableCommand(CommandInterpreter &interpreter, bool enable, const char *name, + const char *help, const char *syntax) + : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable), + m_options_sp(enable ? new EnableOptions() : nullptr) {} + +protected: + void AppendStrictSourcesWarning(CommandReturnObject &result, + const char *source_name) { + if (!source_name) + return; + + // Check if we're *not* using strict sources. If not, + // then the user is going to get debug-level info + // anyways, probably not what they're expecting. + // Unfortunately we can only fix this by adding an + // env var, which would have had to have happened + // already. Thus, a warning is the best we can do here. + StreamString stream; + stream.Printf("darwin-log source settings specify to exclude " + "%s messages, but setting " + "'plugin.structured-data.darwin-log." + "strict-sources' is disabled. This process will " + "automatically have %s messages included. Enable" + " the property and relaunch the target binary to have" + " these messages excluded.", + source_name, source_name); + result.AppendWarning(stream.GetString().c_str()); + } + + bool DoExecute(Args &command, CommandReturnObject &result) override { + // First off, set the global sticky state of enable/disable + // based on this command execution. + s_is_explicitly_enabled = m_enable; + + // Next, if this is an enable, save off the option data. + // We will need it later if a process hasn't been launched or + // attached yet. + if (m_enable) { + // Save off enabled configuration so we can apply these parsed + // options the next time an attach or launch occurs. + DebuggerSP debugger_sp = + GetCommandInterpreter().GetDebugger().shared_from_this(); + SetGlobalEnableOptions(debugger_sp, m_options_sp); + } - int - MatchAttributeIndex(const std::string &attribute_name) - { - auto attribute_count = - sizeof(s_filter_attributes) / sizeof(s_filter_attributes[0]); - for (size_t i = 0; i < attribute_count; ++i) - { - if (attribute_name == s_filter_attributes[i]) - return static_cast<int>(i); - } - - // We didn't match anything. - return -1; - } + // Now check if we have a running process. If so, we should + // instruct the process monitor to enable/disable DarwinLog support + // now. + Target *target = GetSelectedOrDummyTarget(); + if (!target) { + // No target, so there is nothing more to do right now. + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } - static OptionDefinition g_enable_option_table[]; - - bool m_include_debug_level; - bool m_include_info_level; - bool m_include_any_process; - bool m_filter_fall_through_accepts; - bool m_echo_to_stderr; - bool m_display_timestamp_relative; - bool m_display_subsystem; - bool m_display_category; - bool m_display_activity_chain; - bool m_broadcast_events; - bool m_live_stream; - FilterRules m_filter_rules; - }; - - OptionDefinition - EnableOptions::g_enable_option_table[] = - { - // Source stream include/exclude options (the first-level filter). - // This one should be made as small as possible as everything that - // goes through here must be processed by the process monitor. - { LLDB_OPT_SET_ALL, false, "any-process", 'a', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Specifies log messages from other related processes should be " - "included." }, - { LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument, - nullptr, nullptr, 0, eArgTypeNone, - "Specifies debug-level log messages should be included. Specifying" - " --debug implies --info."}, - { LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument, - nullptr, nullptr, 0, eArgTypeNone, - "Specifies info-level log messages should be included." }, - { LLDB_OPT_SET_ALL, false, "filter", 'f', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgRawInput, - // There doesn't appear to be a great way for me to have these - // multi-line, formatted tables in help. This looks mostly right - // but there are extra linefeeds added at seemingly random spots, - // and indentation isn't handled properly on those lines. - "Appends a filter rule to the log message filter chain. Multiple " - "rules may be added by specifying this option multiple times, " - "once per filter rule. Filter rules are processed in the order " - "they are specified, with the --no-match-accepts setting used " - "for any message that doesn't match one of the rules.\n" - "\n" - " Filter spec format:\n" - "\n" - " --filter \"{action} {attribute} {op}\"\n" - "\n" - " {action} :=\n" - " accept |\n" - " reject\n" - "\n" - " {attribute} :=\n" - " activity | // message's most-derived activity\n" - " activity-chain | // message's {parent}:{child} activity\n" - " category | // message's category\n" - " message | // message's expanded contents\n" - " subsystem | // message's subsystem\n" - "\n" - " {op} :=\n" - " match {exact-match-text} |\n" - " regex {search-regex}\n" - "\n" - "The regex flavor used is the C++ std::regex ECMAScript format. " - "Prefer character classes like [[:digit:]] to \\d and the like, as " - "getting the backslashes escaped through properly is error-prone." - }, - { LLDB_OPT_SET_ALL, false, "live-stream", 'l', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, - eArgTypeBoolean, - "Specify whether logging events are live-streamed or buffered. " - "True indicates live streaming, false indicates buffered. The " - "default is true (live streaming). Live streaming will deliver " - "log messages with less delay, but buffered capture mode has less " - "of an observer effect." }, - { LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, - eArgTypeBoolean, - "Specify whether a log message that doesn't match any filter rule " - "is accepted or rejected, where true indicates accept. The " - "default is true." }, - { LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, - eArgTypeBoolean, - "Specify whether os_log()/NSLog() messages are echoed to the " - "target program's stderr. When DarwinLog is enabled, we shut off " - "the mirroring of os_log()/NSLog() to the program's stderr. " - "Setting this flag to true will restore the stderr mirroring." - "The default is false." }, - { LLDB_OPT_SET_ALL, false, "broadcast-events", 'b', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, - eArgTypeBoolean, - "Specify if the plugin should broadcast events. Broadcasting " - "log events is a requirement for displaying the log entries in " - "LLDB command-line. It is also required if LLDB clients want to " - "process log events. The default is true." }, - // Message formatting options - { LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Include timestamp in the message header when printing a log " - "message. The timestamp is relative to the first displayed " - "message." }, - { LLDB_OPT_SET_ALL, false, "subsystem", 's', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Include the subsystem in the the message header when displaying " - "a log message." }, - { LLDB_OPT_SET_ALL, false, "category", 'c', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Include the category in the the message header when displaying " - "a log message." }, - { LLDB_OPT_SET_ALL, false, "activity-chain", 'C', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Include the activity parent-child chain in the the message header " - "when displaying a log message. The activity hierarchy is " - "displayed as {grandparent-activity}:" - "{parent-activity}:{activity}[:...]."}, - { LLDB_OPT_SET_ALL, false, "all-fields", 'A', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, - "Shortcut to specify that all header fields should be displayed." }, - - // Tail sentinel entry - { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } - }; - - class EnableCommand : public CommandObjectParsed - { - public: - - EnableCommand(CommandInterpreter &interpreter, bool enable, - const char *name, const char *help, - const char *syntax) : - CommandObjectParsed(interpreter, name, help, syntax), - m_enable(enable), - m_options_sp(enable ? new EnableOptions() : nullptr) - { - } + // Grab the active process. + auto process_sp = target->GetProcessSP(); + if (!process_sp) { + // No active process, so there is nothing more to do right + // now. + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } - protected: - - void - AppendStrictSourcesWarning(CommandReturnObject &result, - const char *source_name) - { - if (!source_name) - return; - - // Check if we're *not* using strict sources. If not, - // then the user is going to get debug-level info - // anyways, probably not what they're expecting. - // Unfortunately we can only fix this by adding an - // env var, which would have had to have happened - // already. Thus, a warning is the best we can do here. - StreamString stream; - stream.Printf("darwin-log source settings specify to exclude " - "%s messages, but setting " - "'plugin.structured-data.darwin-log." - "strict-sources' is disabled. This process will " - "automatically have %s messages included. Enable" - " the property and relaunch the target binary to have" - " these messages excluded.", source_name, - source_name); - result.AppendWarning(stream.GetString().c_str()); - } + // If the process is no longer alive, we can't do this now. + // We'll catch it the next time the process is started up. + if (!process_sp->IsAlive()) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } - bool - DoExecute (Args& command, CommandReturnObject &result) override - { - // First off, set the global sticky state of enable/disable - // based on this command execution. - s_is_explicitly_enabled = m_enable; - - // Next, if this is an enable, save off the option data. - // We will need it later if a process hasn't been launched or - // attached yet. - if (m_enable) - { - // Save off enabled configuration so we can apply these parsed - // options the next time an attach or launch occurs. - DebuggerSP debugger_sp = - GetCommandInterpreter().GetDebugger().shared_from_this(); - SetGlobalEnableOptions(debugger_sp, m_options_sp); - } - - // Now check if we have a running process. If so, we should - // instruct the process monitor to enable/disable DarwinLog support - // now. - Target *target = GetSelectedOrDummyTarget(); - if (!target) - { - // No target, so there is nothing more to do right now. - result.SetStatus(eReturnStatusSuccessFinishNoResult); - return true; - } - - // Grab the active process. - auto process_sp = target->GetProcessSP(); - if (!process_sp) - { - // No active process, so there is nothing more to do right - // now. - result.SetStatus(eReturnStatusSuccessFinishNoResult); - return true; - } - - // If the process is no longer alive, we can't do this now. - // We'll catch it the next time the process is started up. - if (!process_sp->IsAlive()) - { - result.SetStatus(eReturnStatusSuccessFinishNoResult); - return true; - } - - // Get the plugin for the process. - auto plugin_sp = - process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); - if (!plugin_sp || - (plugin_sp->GetPluginName() != - StructuredDataDarwinLog::GetStaticPluginName())) - { - result.AppendError("failed to get StructuredDataPlugin for " - "the process"); - result.SetStatus(eReturnStatusFailed); - } - StructuredDataDarwinLog &plugin = - *static_cast<StructuredDataDarwinLog*>(plugin_sp.get()); - - if (m_enable) - { - // To do this next part right, we need to track whether we - // added the proper environment variable at launch time. - // It is incorrect to assume that we're enabling after launch, - // and that therefore if we needed the env var set to properly - // handle the options, that it is set incorrectly. The env var - // could have been added if this is a re-enable using different - // arguments. This is a bit tricky as the point where we - // have to set the env var, we don't yet have a process or the - // associated darwin-log plugin instance, and thus don't have a - // great place to stick this knowledge. + // Get the plugin for the process. + auto plugin_sp = + process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); + if (!plugin_sp || (plugin_sp->GetPluginName() != + StructuredDataDarwinLog::GetStaticPluginName())) { + result.AppendError("failed to get StructuredDataPlugin for " + "the process"); + result.SetStatus(eReturnStatusFailed); + } + StructuredDataDarwinLog &plugin = + *static_cast<StructuredDataDarwinLog *>(plugin_sp.get()); + + if (m_enable) { +// To do this next part right, we need to track whether we +// added the proper environment variable at launch time. +// It is incorrect to assume that we're enabling after launch, +// and that therefore if we needed the env var set to properly +// handle the options, that it is set incorrectly. The env var +// could have been added if this is a re-enable using different +// arguments. This is a bit tricky as the point where we +// have to set the env var, we don't yet have a process or the +// associated darwin-log plugin instance, and thus don't have a +// great place to stick this knowledge. #if 0 // Check if we're attempting to disable debug-level or // info-level content but we haven't launched with the magic @@ -1133,273 +893,233 @@ namespace sddarwinlog_private } #endif - // Hook up the breakpoint for the process that detects when - // libtrace has been sufficiently initialized to really start - // the os_log stream. This is insurance to assure us that - // logging is really enabled. Requesting that logging be - // enabled for a process before libtrace is initialized - // results in a scenario where no errors occur, but no logging - // is captured, either. This step is to eliminate that - // possibility. - plugin.AddInitCompletionHook(*process_sp.get()); - } - - // Send configuration to the feature by way of the process. - // Construct the options we will use. - auto config_sp = m_options_sp->BuildConfigurationData(m_enable); - const Error error = - process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), - config_sp); - - // Report results. - if (!error.Success()) - { - result.AppendError(error.AsCString()); - result.SetStatus(eReturnStatusFailed); - // Our configuration failed, so we're definitely disabled. - plugin.SetEnabled(false); - } - else - { - result.SetStatus(eReturnStatusSuccessFinishNoResult); - // Our configuration succeeeded, so we're enabled/disabled - // per whichever one this command is setup to do. - plugin.SetEnabled(m_enable); - } - return result.Succeeded(); - } - - Options* - GetOptions () override - { - // We don't have options when this represents disable. - return m_enable ? m_options_sp.get() : nullptr; - } - - private: - const bool m_enable; - EnableOptionsSP m_options_sp; - }; + // Hook up the breakpoint for the process that detects when + // libtrace has been sufficiently initialized to really start + // the os_log stream. This is insurance to assure us that + // logging is really enabled. Requesting that logging be + // enabled for a process before libtrace is initialized + // results in a scenario where no errors occur, but no logging + // is captured, either. This step is to eliminate that + // possibility. + plugin.AddInitCompletionHook(*process_sp.get()); + } - // ------------------------------------------------------------------------- - /// Provides the status command. - // ------------------------------------------------------------------------- - class StatusCommand : public CommandObjectParsed - { - public: + // Send configuration to the feature by way of the process. + // Construct the options we will use. + auto config_sp = m_options_sp->BuildConfigurationData(m_enable); + const Error error = + process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp); - StatusCommand(CommandInterpreter &interpreter) : - CommandObjectParsed(interpreter, "status", + // Report results. + if (!error.Success()) { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + // Our configuration failed, so we're definitely disabled. + plugin.SetEnabled(false); + } else { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + // Our configuration succeeeded, so we're enabled/disabled + // per whichever one this command is setup to do. + plugin.SetEnabled(m_enable); + } + return result.Succeeded(); + } + + Options *GetOptions() override { + // We don't have options when this represents disable. + return m_enable ? m_options_sp.get() : nullptr; + } + +private: + const bool m_enable; + EnableOptionsSP m_options_sp; +}; + +// ------------------------------------------------------------------------- +/// Provides the status command. +// ------------------------------------------------------------------------- +class StatusCommand : public CommandObjectParsed { +public: + StatusCommand(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "status", "Show whether Darwin log supported is available" " and enabled.", - "plugin structured-data darwin-log status") - { - } - - protected: - - bool - DoExecute (Args& command, CommandReturnObject &result) override - { - auto &stream = result.GetOutputStream(); - - // Figure out if we've got a process. If so, we can tell if - // DarwinLog is available for that process. - Target *target = GetSelectedOrDummyTarget(); - auto process_sp = target ? target->GetProcessSP() : ProcessSP(); - if (!target || !process_sp) - { - stream.PutCString("Availability: unknown (requires process)\n"); - stream.PutCString("Enabled: not applicable " - "(requires process)\n"); - } - else - { - auto plugin_sp = - process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); - stream.Printf("Availability: %s\n", plugin_sp ? "available" : - "unavailable"); - auto &plugin_name = - StructuredDataDarwinLog::GetStaticPluginName(); - const bool enabled = plugin_sp ? - plugin_sp->GetEnabled(plugin_name) : false; - stream.Printf("Enabled: %s\n", enabled ? "true" : - "false"); - } - - // Display filter settings. - DebuggerSP debugger_sp = - GetCommandInterpreter().GetDebugger().shared_from_this(); - auto options_sp = GetGlobalEnableOptions(debugger_sp); - if (!options_sp) - { - // Nothing more to do. - result.SetStatus(eReturnStatusSuccessFinishResult); - return true; - } - - // Print filter rules - stream.PutCString("DarwinLog filter rules:\n"); - - stream.IndentMore(); - - if (options_sp->GetFilterRules().empty()) - { - stream.Indent(); - stream.PutCString("none\n"); - } - else - { - // Print each of the filter rules. - int rule_number = 0; - for (auto rule_sp : options_sp->GetFilterRules()) - { - ++rule_number; - if (!rule_sp) - continue; - - stream.Indent(); - stream.Printf("%02d: ", rule_number); - rule_sp->Dump(stream); - stream.PutChar('\n'); - } - } - stream.IndentLess(); - - // Print no-match handling. - stream.Indent(); - stream.Printf("no-match behavior: %s\n", - options_sp->GetFallthroughAccepts() ? "accept" : - "reject"); - - result.SetStatus(eReturnStatusSuccessFinishResult); - return true; - } - }; - - // ------------------------------------------------------------------------- - /// Provides the darwin-log base command - // ------------------------------------------------------------------------- - class BaseCommand : public CommandObjectMultiword - { - public: - BaseCommand(CommandInterpreter &interpreter) : - CommandObjectMultiword(interpreter, - "plugin structured-data darwin-log", - "Commands for configuring Darwin os_log " - "support.", - "") - { - // enable - auto enable_help = "Enable Darwin log collection, or re-enable " - "with modified configuration."; - auto enable_syntax = "plugin structured-data darwin-log enable"; - auto enable_cmd_sp = - CommandObjectSP(new EnableCommand(interpreter, - true, // enable - "enable", - enable_help, - enable_syntax)); - LoadSubCommand("enable", enable_cmd_sp); - - // disable - auto disable_help = "Disable Darwin log collection."; - auto disable_syntax = "plugin structured-data darwin-log disable"; - auto disable_cmd_sp = - CommandObjectSP(new EnableCommand(interpreter, - false, // disable - "disable", - disable_help, - disable_syntax)); - LoadSubCommand("disable", disable_cmd_sp); - - // status - auto status_cmd_sp = - CommandObjectSP(new StatusCommand(interpreter)); - LoadSubCommand("status", status_cmd_sp); - } - }; - - EnableOptionsSP - ParseAutoEnableOptions(Error &error, Debugger &debugger) - { - // We are abusing the options data model here so that we can parse - // options without requiring the Debugger instance. - - // We have an empty execution context at this point. We only want - // to parse options, and we don't need any context to do this here. - // In fact, we want to be able to parse the enable options before having - // any context. - ExecutionContext exe_ctx; - - EnableOptionsSP options_sp(new EnableOptions()); - options_sp->NotifyOptionParsingStarting(&exe_ctx); - - CommandReturnObject result; - - // Parse the arguments. - auto options_property_sp = - debugger.GetPropertyValue(nullptr, - "plugin.structured-data.darwin-log." - "auto-enable-options", - false, - error); - if (!error.Success()) - return EnableOptionsSP(); - if (!options_property_sp) - { - error.SetErrorString("failed to find option setting for " - "plugin.structured-data.darwin-log."); - return EnableOptionsSP(); - } - - const char *enable_options = options_property_sp->GetAsString()->GetCurrentValue(); - Args args(enable_options); - if (args.GetArgumentCount() > 0) - { - // Eliminate the initial '--' that would be required to set the - // settings that themselves include '-' and/or '--'. - const char *first_arg = args.GetArgumentAtIndex(0); - if (first_arg && (strcmp(first_arg, "--") == 0)) - args.Shift(); - } - - // ParseOptions calls getopt_long_only, which always skips the zero'th item in the array and starts at position 1, - // so we need to push a dummy value into position zero. - args.Unshift("dummy_string"); - bool require_validation = false; - error = args.ParseOptions(*options_sp.get(), &exe_ctx, PlatformSP(), - require_validation); - if (!error.Success()) - return EnableOptionsSP(); - - if (!options_sp->VerifyOptions(result)) - return EnableOptionsSP(); - - // We successfully parsed and validated the options. - return options_sp; + "plugin structured-data darwin-log status") {} + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + auto &stream = result.GetOutputStream(); + + // Figure out if we've got a process. If so, we can tell if + // DarwinLog is available for that process. + Target *target = GetSelectedOrDummyTarget(); + auto process_sp = target ? target->GetProcessSP() : ProcessSP(); + if (!target || !process_sp) { + stream.PutCString("Availability: unknown (requires process)\n"); + stream.PutCString("Enabled: not applicable " + "(requires process)\n"); + } else { + auto plugin_sp = + process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); + stream.Printf("Availability: %s\n", + plugin_sp ? "available" : "unavailable"); + auto &plugin_name = StructuredDataDarwinLog::GetStaticPluginName(); + const bool enabled = + plugin_sp ? plugin_sp->GetEnabled(plugin_name) : false; + stream.Printf("Enabled: %s\n", enabled ? "true" : "false"); } - bool - RunEnableCommand(CommandInterpreter &interpreter) - { - StreamString command_stream; - - command_stream << "plugin structured-data darwin-log enable"; - auto enable_options = GetGlobalProperties()->GetAutoEnableOptions(); - if (enable_options && (strlen(enable_options) > 0)) - { - command_stream << ' '; - command_stream << enable_options; - } + // Display filter settings. + DebuggerSP debugger_sp = + GetCommandInterpreter().GetDebugger().shared_from_this(); + auto options_sp = GetGlobalEnableOptions(debugger_sp); + if (!options_sp) { + // Nothing more to do. + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } - // Run the command. - CommandReturnObject return_object; - interpreter.HandleCommand(command_stream.GetString().c_str(), - eLazyBoolNo, - return_object); - return return_object.Succeeded(); + // Print filter rules + stream.PutCString("DarwinLog filter rules:\n"); + + stream.IndentMore(); + + if (options_sp->GetFilterRules().empty()) { + stream.Indent(); + stream.PutCString("none\n"); + } else { + // Print each of the filter rules. + int rule_number = 0; + for (auto rule_sp : options_sp->GetFilterRules()) { + ++rule_number; + if (!rule_sp) + continue; + + stream.Indent(); + stream.Printf("%02d: ", rule_number); + rule_sp->Dump(stream); + stream.PutChar('\n'); + } } + stream.IndentLess(); + + // Print no-match handling. + stream.Indent(); + stream.Printf("no-match behavior: %s\n", + options_sp->GetFallthroughAccepts() ? "accept" : "reject"); + + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +// ------------------------------------------------------------------------- +/// Provides the darwin-log base command +// ------------------------------------------------------------------------- +class BaseCommand : public CommandObjectMultiword { +public: + BaseCommand(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "plugin structured-data darwin-log", + "Commands for configuring Darwin os_log " + "support.", + "") { + // enable + auto enable_help = "Enable Darwin log collection, or re-enable " + "with modified configuration."; + auto enable_syntax = "plugin structured-data darwin-log enable"; + auto enable_cmd_sp = CommandObjectSP( + new EnableCommand(interpreter, + true, // enable + "enable", enable_help, enable_syntax)); + LoadSubCommand("enable", enable_cmd_sp); + + // disable + auto disable_help = "Disable Darwin log collection."; + auto disable_syntax = "plugin structured-data darwin-log disable"; + auto disable_cmd_sp = CommandObjectSP( + new EnableCommand(interpreter, + false, // disable + "disable", disable_help, disable_syntax)); + LoadSubCommand("disable", disable_cmd_sp); + + // status + auto status_cmd_sp = CommandObjectSP(new StatusCommand(interpreter)); + LoadSubCommand("status", status_cmd_sp); + } +}; + +EnableOptionsSP ParseAutoEnableOptions(Error &error, Debugger &debugger) { + // We are abusing the options data model here so that we can parse + // options without requiring the Debugger instance. + + // We have an empty execution context at this point. We only want + // to parse options, and we don't need any context to do this here. + // In fact, we want to be able to parse the enable options before having + // any context. + ExecutionContext exe_ctx; + + EnableOptionsSP options_sp(new EnableOptions()); + options_sp->NotifyOptionParsingStarting(&exe_ctx); + + CommandReturnObject result; + + // Parse the arguments. + auto options_property_sp = + debugger.GetPropertyValue(nullptr, "plugin.structured-data.darwin-log." + "auto-enable-options", + false, error); + if (!error.Success()) + return EnableOptionsSP(); + if (!options_property_sp) { + error.SetErrorString("failed to find option setting for " + "plugin.structured-data.darwin-log."); + return EnableOptionsSP(); + } + + const char *enable_options = + options_property_sp->GetAsString()->GetCurrentValue(); + Args args(enable_options); + if (args.GetArgumentCount() > 0) { + // Eliminate the initial '--' that would be required to set the + // settings that themselves include '-' and/or '--'. + const char *first_arg = args.GetArgumentAtIndex(0); + if (first_arg && (strcmp(first_arg, "--") == 0)) + args.Shift(); + } + + // ParseOptions calls getopt_long_only, which always skips the zero'th item in + // the array and starts at position 1, + // so we need to push a dummy value into position zero. + args.Unshift("dummy_string"); + bool require_validation = false; + error = args.ParseOptions(*options_sp.get(), &exe_ctx, PlatformSP(), + require_validation); + if (!error.Success()) + return EnableOptionsSP(); + + if (!options_sp->VerifyOptions(result)) + return EnableOptionsSP(); + + // We successfully parsed and validated the options. + return options_sp; +} + +bool RunEnableCommand(CommandInterpreter &interpreter) { + StreamString command_stream; + + command_stream << "plugin structured-data darwin-log enable"; + auto enable_options = GetGlobalProperties()->GetAutoEnableOptions(); + if (enable_options && (strlen(enable_options) > 0)) { + command_stream << ' '; + command_stream << enable_options; + } + + // Run the command. + CommandReturnObject return_object; + interpreter.HandleCommand(command_stream.GetString().c_str(), eLazyBoolNo, + return_object); + return return_object.Succeeded(); +} } using namespace sddarwinlog_private; @@ -1410,28 +1130,20 @@ using namespace sddarwinlog_private; // Public static API // ----------------------------------------------------------------------------- -void -StructuredDataDarwinLog::Initialize() -{ - RegisterFilterOperations(); - PluginManager::RegisterPlugin(GetStaticPluginName(), - "Darwin os_log() and os_activity() support", - &CreateInstance, - &DebuggerInitialize, - &FilterLaunchInfo); +void StructuredDataDarwinLog::Initialize() { + RegisterFilterOperations(); + PluginManager::RegisterPlugin( + GetStaticPluginName(), "Darwin os_log() and os_activity() support", + &CreateInstance, &DebuggerInitialize, &FilterLaunchInfo); } -void -StructuredDataDarwinLog::Terminate() -{ - PluginManager::UnregisterPlugin(&CreateInstance); +void StructuredDataDarwinLog::Terminate() { + PluginManager::UnregisterPlugin(&CreateInstance); } -const ConstString& -StructuredDataDarwinLog::GetStaticPluginName() -{ - static ConstString s_plugin_name("darwin-log"); - return s_plugin_name; +const ConstString &StructuredDataDarwinLog::GetStaticPluginName() { + static ConstString s_plugin_name("darwin-log"); + return s_plugin_name; } #pragma mark - @@ -1441,17 +1153,11 @@ StructuredDataDarwinLog::GetStaticPluginName() // PluginInterface API // ----------------------------------------------------------------------------- -ConstString -StructuredDataDarwinLog::GetPluginName() -{ - return GetStaticPluginName(); +ConstString StructuredDataDarwinLog::GetPluginName() { + return GetStaticPluginName(); } -uint32_t -StructuredDataDarwinLog::GetPluginVersion() -{ - return 1; -} +uint32_t StructuredDataDarwinLog::GetPluginVersion() { return 1; } #pragma mark - #pragma mark StructuredDataPlugin API @@ -1460,292 +1166,253 @@ StructuredDataDarwinLog::GetPluginVersion() // StructuredDataPlugin API // ----------------------------------------------------------------------------- -bool -StructuredDataDarwinLog::SupportsStructuredDataType( - const ConstString &type_name) -{ - return type_name == GetDarwinLogTypeName(); +bool StructuredDataDarwinLog::SupportsStructuredDataType( + const ConstString &type_name) { + return type_name == GetDarwinLogTypeName(); } -void -StructuredDataDarwinLog::HandleArrivalOfStructuredData(Process &process, - const ConstString - &type_name, - const - StructuredData::ObjectSP - &object_sp) -{ - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - if (log) - { - StreamString json_stream; - if (object_sp) - object_sp->Dump(json_stream); - else - json_stream.PutCString("<null>"); - log->Printf("StructuredDataDarwinLog::%s() called with json: %s", - __FUNCTION__, json_stream.GetString().c_str()); - } - - // Ignore empty structured data. - if (!object_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() StructuredData object " - "is null", __FUNCTION__); - return; - } - - // Ignore any data that isn't for us. - if (type_name != GetDarwinLogTypeName()) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() StructuredData type " - "expected to be %s but was %s, ignoring", __FUNCTION__, - GetDarwinLogTypeName().AsCString(), - type_name.AsCString()); - return; - } +void StructuredDataDarwinLog::HandleArrivalOfStructuredData( + Process &process, const ConstString &type_name, + const StructuredData::ObjectSP &object_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) { + StreamString json_stream; + if (object_sp) + object_sp->Dump(json_stream); + else + json_stream.PutCString("<null>"); + log->Printf("StructuredDataDarwinLog::%s() called with json: %s", + __FUNCTION__, json_stream.GetString().c_str()); + } - // Broadcast the structured data event if we have that enabled. - // This is the way that the outside world (all clients) get access - // to this data. This plugin sets policy as to whether we do that. - DebuggerSP debugger_sp = - process.GetTarget().GetDebugger().shared_from_this(); - auto options_sp = GetGlobalEnableOptions(debugger_sp); - if (options_sp && options_sp->GetBroadcastEvents()) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() broadcasting event", - __FUNCTION__); - process.BroadcastStructuredData(object_sp, shared_from_this()); - } + // Ignore empty structured data. + if (!object_sp) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() StructuredData object " + "is null", + __FUNCTION__); + return; + } + + // Ignore any data that isn't for us. + if (type_name != GetDarwinLogTypeName()) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() StructuredData type " + "expected to be %s but was %s, ignoring", + __FUNCTION__, GetDarwinLogTypeName().AsCString(), + type_name.AsCString()); + return; + } + + // Broadcast the structured data event if we have that enabled. + // This is the way that the outside world (all clients) get access + // to this data. This plugin sets policy as to whether we do that. + DebuggerSP debugger_sp = process.GetTarget().GetDebugger().shared_from_this(); + auto options_sp = GetGlobalEnableOptions(debugger_sp); + if (options_sp && options_sp->GetBroadcastEvents()) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() broadcasting event", + __FUNCTION__); + process.BroadcastStructuredData(object_sp, shared_from_this()); + } - // Later, hang on to a configurable amount of these and allow commands - // to inspect, including showing backtraces. + // Later, hang on to a configurable amount of these and allow commands + // to inspect, including showing backtraces. } -static void -SetErrorWithJSON(Error &error, const char *message, - StructuredData::Object &object) -{ - if (!message) - { - error.SetErrorString("Internal error: message not set."); - return; - } +static void SetErrorWithJSON(Error &error, const char *message, + StructuredData::Object &object) { + if (!message) { + error.SetErrorString("Internal error: message not set."); + return; + } - StreamString object_stream; - object.Dump(object_stream); - object_stream.Flush(); + StreamString object_stream; + object.Dump(object_stream); + object_stream.Flush(); - error.SetErrorStringWithFormat("%s: %s", message, - object_stream.GetString().c_str()); + error.SetErrorStringWithFormat("%s: %s", message, + object_stream.GetString().c_str()); } -Error -StructuredDataDarwinLog::GetDescription(const StructuredData::ObjectSP - &object_sp, - lldb_private::Stream &stream) -{ - Error error; - - if (!object_sp) - { - error.SetErrorString("No structured data."); - return error; - } - - // Log message payload objects will be dictionaries. - const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary(); - if (!dictionary) - { - SetErrorWithJSON(error, "Structured data should have been a dictionary " - "but wasn't", *object_sp); - return error; - } - - // Validate this is really a message for our plugin. - ConstString type_name; - if (!dictionary->GetValueForKeyAsString("type", type_name)) - { - SetErrorWithJSON(error, "Structured data doesn't contain mandatory " - "type field", *object_sp); - return error; - } +Error StructuredDataDarwinLog::GetDescription( + const StructuredData::ObjectSP &object_sp, lldb_private::Stream &stream) { + Error error; - if (type_name != GetDarwinLogTypeName()) - { - // This is okay - it simply means the data we received is not a log - // message. We'll just format it as is. - object_sp->Dump(stream); - return error; - } + if (!object_sp) { + error.SetErrorString("No structured data."); + return error; + } + + // Log message payload objects will be dictionaries. + const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary(); + if (!dictionary) { + SetErrorWithJSON(error, "Structured data should have been a dictionary " + "but wasn't", + *object_sp); + return error; + } + + // Validate this is really a message for our plugin. + ConstString type_name; + if (!dictionary->GetValueForKeyAsString("type", type_name)) { + SetErrorWithJSON(error, "Structured data doesn't contain mandatory " + "type field", + *object_sp); + return error; + } - // DarwinLog dictionaries store their data - // in an array with key name "events". - StructuredData::Array *events = nullptr; - if (!dictionary->GetValueForKeyAsArray("events", events) || - !events) - { - SetErrorWithJSON(error, "Log structured data is missing mandatory " - "'events' field, expected to be an array", *object_sp); - return error; - } + if (type_name != GetDarwinLogTypeName()) { + // This is okay - it simply means the data we received is not a log + // message. We'll just format it as is. + object_sp->Dump(stream); + return error; + } + + // DarwinLog dictionaries store their data + // in an array with key name "events". + StructuredData::Array *events = nullptr; + if (!dictionary->GetValueForKeyAsArray("events", events) || !events) { + SetErrorWithJSON(error, "Log structured data is missing mandatory " + "'events' field, expected to be an array", + *object_sp); + return error; + } - events->ForEach([&stream, &error, &object_sp, this] - (StructuredData::Object *object) - { - if (!object) - { - // Invalid. Stop iterating. - SetErrorWithJSON(error, "Log event entry is null", *object_sp); - return false; + events->ForEach( + [&stream, &error, &object_sp, this](StructuredData::Object *object) { + if (!object) { + // Invalid. Stop iterating. + SetErrorWithJSON(error, "Log event entry is null", *object_sp); + return false; } auto event = object->GetAsDictionary(); - if (!event) - { - // Invalid, stop iterating. - SetErrorWithJSON(error, "Log event is not a dictionary", - *object_sp); - return false; + if (!event) { + // Invalid, stop iterating. + SetErrorWithJSON(error, "Log event is not a dictionary", *object_sp); + return false; } // If we haven't already grabbed the first timestamp // value, do that now. - if (!m_recorded_first_timestamp) - { - uint64_t timestamp = 0; - if (event->GetValueForKeyAsInteger("timestamp", - timestamp)) - { - m_first_timestamp_seen = timestamp; - m_recorded_first_timestamp = true; - } + if (!m_recorded_first_timestamp) { + uint64_t timestamp = 0; + if (event->GetValueForKeyAsInteger("timestamp", timestamp)) { + m_first_timestamp_seen = timestamp; + m_recorded_first_timestamp = true; + } } HandleDisplayOfEvent(*event, stream); return true; - }); + }); - stream.Flush(); - return error; + stream.Flush(); + return error; } -bool -StructuredDataDarwinLog::GetEnabled(const ConstString &type_name) const -{ - if (type_name == GetStaticPluginName()) - return m_is_enabled; - else - return false; +bool StructuredDataDarwinLog::GetEnabled(const ConstString &type_name) const { + if (type_name == GetStaticPluginName()) + return m_is_enabled; + else + return false; } -void -StructuredDataDarwinLog::SetEnabled(bool enabled) -{ - m_is_enabled = enabled; +void StructuredDataDarwinLog::SetEnabled(bool enabled) { + m_is_enabled = enabled; } -void -StructuredDataDarwinLog::ModulesDidLoad(Process &process, - ModuleList &module_list) -{ - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); +void StructuredDataDarwinLog::ModulesDidLoad(Process &process, + ModuleList &module_list) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("StructuredDataDarwinLog::%s called (process uid %u)", + __FUNCTION__, process.GetUniqueID()); + + // Check if we should enable the darwin log support on startup/attach. + if (!GetGlobalProperties()->GetEnableOnStartup() && + !s_is_explicitly_enabled) { + // We're neither auto-enabled or explicitly enabled, so we shouldn't + // try to enable here. if (log) - log->Printf("StructuredDataDarwinLog::%s called (process uid %u)", + log->Printf("StructuredDataDarwinLog::%s not applicable, we're not " + "enabled (process uid %u)", + __FUNCTION__, process.GetUniqueID()); + return; + } + + // If we already added the breakpoint, we've got nothing left to do. + { + std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex); + if (m_added_breakpoint) { + if (log) + log->Printf("StructuredDataDarwinLog::%s process uid %u's " + "post-libtrace-init breakpoint is already set", __FUNCTION__, process.GetUniqueID()); - - // Check if we should enable the darwin log support on startup/attach. - if (!GetGlobalProperties()->GetEnableOnStartup() && - !s_is_explicitly_enabled) - { - // We're neither auto-enabled or explicitly enabled, so we shouldn't - // try to enable here. - if (log) - log->Printf("StructuredDataDarwinLog::%s not applicable, we're not " - "enabled (process uid %u)", __FUNCTION__, - process.GetUniqueID()); - return; - } - - // If we already added the breakpoint, we've got nothing left to do. - { - std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex); - if (m_added_breakpoint) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s process uid %u's " - "post-libtrace-init breakpoint is already set", - __FUNCTION__, process.GetUniqueID()); - return; - } - } - - // The logging support module name, specifies the name of - // the image name that must be loaded into the debugged process before - // we can try to enable logging. - const char *logging_module_cstr = - GetGlobalProperties()->GetLoggingModuleName(); - if (!logging_module_cstr || (logging_module_cstr[0] == 0)) - { - // We need this. Bail. - if (log) - log->Printf("StructuredDataDarwinLog::%s no logging module name " - "specified, we don't know where to set a breakpoint " - "(process uid %u)", __FUNCTION__, - process.GetUniqueID()); - return; + return; } - - const ConstString logging_module_name = - ConstString(logging_module_cstr); - - // We need to see libtrace in the list of modules before we can enable - // tracing for the target process. - bool found_logging_support_module = false; - for (size_t i = 0; i < module_list.GetSize(); ++i) - { - auto module_sp = module_list.GetModuleAtIndex(i); - if (!module_sp) - continue; - - auto &file_spec = module_sp->GetFileSpec(); - found_logging_support_module = - (file_spec.GetLastPathComponent() == logging_module_name); - if (found_logging_support_module) - break; - } - - if (!found_logging_support_module) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s logging module %s " - "has not yet been loaded, can't set a breakpoint " - "yet (process uid %u)", __FUNCTION__, - logging_module_name.AsCString(), - process.GetUniqueID()); - return; - } - - // Time to enqueue the breakpoint so we can wait for logging support - // to be initialized before we try to tap the libtrace stream. - AddInitCompletionHook(process); + } + + // The logging support module name, specifies the name of + // the image name that must be loaded into the debugged process before + // we can try to enable logging. + const char *logging_module_cstr = + GetGlobalProperties()->GetLoggingModuleName(); + if (!logging_module_cstr || (logging_module_cstr[0] == 0)) { + // We need this. Bail. + if (log) + log->Printf("StructuredDataDarwinLog::%s no logging module name " + "specified, we don't know where to set a breakpoint " + "(process uid %u)", + __FUNCTION__, process.GetUniqueID()); + return; + } + + const ConstString logging_module_name = ConstString(logging_module_cstr); + + // We need to see libtrace in the list of modules before we can enable + // tracing for the target process. + bool found_logging_support_module = false; + for (size_t i = 0; i < module_list.GetSize(); ++i) { + auto module_sp = module_list.GetModuleAtIndex(i); + if (!module_sp) + continue; + + auto &file_spec = module_sp->GetFileSpec(); + found_logging_support_module = + (file_spec.GetLastPathComponent() == logging_module_name); + if (found_logging_support_module) + break; + } + + if (!found_logging_support_module) { if (log) - log->Printf("StructuredDataDarwinLog::%s post-init hook breakpoint " - "set for logging module %s (process uid %u)", __FUNCTION__, - logging_module_name.AsCString(), - process.GetUniqueID()); - - // We need to try the enable here as well, which will succeed - // in the event that we're attaching to (rather than launching) the - // process and the process is already past initialization time. In that - // case, the completion breakpoint will never get hit and therefore won't - // start that way. It doesn't hurt much beyond a bit of bandwidth - // if we end up doing this twice. It hurts much more if we don't get - // the logging enabled when the user expects it. - EnableNow(); + log->Printf("StructuredDataDarwinLog::%s logging module %s " + "has not yet been loaded, can't set a breakpoint " + "yet (process uid %u)", + __FUNCTION__, logging_module_name.AsCString(), + process.GetUniqueID()); + return; + } + + // Time to enqueue the breakpoint so we can wait for logging support + // to be initialized before we try to tap the libtrace stream. + AddInitCompletionHook(process); + if (log) + log->Printf("StructuredDataDarwinLog::%s post-init hook breakpoint " + "set for logging module %s (process uid %u)", + __FUNCTION__, logging_module_name.AsCString(), + process.GetUniqueID()); + + // We need to try the enable here as well, which will succeed + // in the event that we're attaching to (rather than launching) the + // process and the process is already past initialization time. In that + // case, the completion breakpoint will never get hit and therefore won't + // start that way. It doesn't hurt much beyond a bit of bandwidth + // if we end up doing this twice. It hurts much more if we don't get + // the logging enabled when the user expects it. + EnableNow(); } #pragma mark - @@ -1755,666 +1422,592 @@ StructuredDataDarwinLog::ModulesDidLoad(Process &process, // Private constructors // ----------------------------------------------------------------------------- -StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp) : - StructuredDataPlugin(process_wp), - m_recorded_first_timestamp(false), - m_first_timestamp_seen(0), - m_is_enabled(false), - m_added_breakpoint_mutex(), - m_added_breakpoint() -{ -} +StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp) + : StructuredDataPlugin(process_wp), m_recorded_first_timestamp(false), + m_first_timestamp_seen(0), m_is_enabled(false), + m_added_breakpoint_mutex(), m_added_breakpoint() {} // ----------------------------------------------------------------------------- // Private static methods // ----------------------------------------------------------------------------- StructuredDataPluginSP -StructuredDataDarwinLog::CreateInstance(Process &process) -{ - // Currently only Apple targets support the os_log/os_activity - // protocol. - if (process.GetTarget().GetArchitecture().GetTriple().getVendor() == - llvm::Triple::VendorType::Apple) - { - auto process_wp = ProcessWP(process.shared_from_this()); - return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp)); - } - else - { - return StructuredDataPluginSP(); - } +StructuredDataDarwinLog::CreateInstance(Process &process) { + // Currently only Apple targets support the os_log/os_activity + // protocol. + if (process.GetTarget().GetArchitecture().GetTriple().getVendor() == + llvm::Triple::VendorType::Apple) { + auto process_wp = ProcessWP(process.shared_from_this()); + return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp)); + } else { + return StructuredDataPluginSP(); + } } -void -StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger) -{ - // Setup parent class first. - StructuredDataPlugin::InitializeBasePluginForDebugger(debugger); - - // Get parent command. - auto &interpreter = debugger.GetCommandInterpreter(); - std::string parent_command_text = "plugin structured-data"; - auto parent_command = - interpreter.GetCommandObjectForCommand(parent_command_text); - if (!parent_command) - { - // Ut oh, parent failed to create parent command. - // TODO log - return; - } - - auto command_name = "darwin-log"; - auto command_sp = CommandObjectSP(new BaseCommand(interpreter)); - bool result = parent_command->LoadSubCommand(command_name, command_sp); - if (!result) - { - // TODO log it once we setup structured data logging - } - - if (!PluginManager::GetSettingForPlatformPlugin(debugger, - StructuredDataDarwinLogProperties::GetSettingName())) - { - const bool is_global_setting = true; - PluginManager::CreateSettingForStructuredDataPlugin(debugger, - GetGlobalProperties()->GetValueProperties(), - ConstString ("Properties for the darwin-log" - " plug-in."), - is_global_setting); - } - +void StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger) { + // Setup parent class first. + StructuredDataPlugin::InitializeBasePluginForDebugger(debugger); + + // Get parent command. + auto &interpreter = debugger.GetCommandInterpreter(); + std::string parent_command_text = "plugin structured-data"; + auto parent_command = + interpreter.GetCommandObjectForCommand(parent_command_text); + if (!parent_command) { + // Ut oh, parent failed to create parent command. + // TODO log + return; + } + + auto command_name = "darwin-log"; + auto command_sp = CommandObjectSP(new BaseCommand(interpreter)); + bool result = parent_command->LoadSubCommand(command_name, command_sp); + if (!result) { + // TODO log it once we setup structured data logging + } + + if (!PluginManager::GetSettingForPlatformPlugin( + debugger, StructuredDataDarwinLogProperties::GetSettingName())) { + const bool is_global_setting = true; + PluginManager::CreateSettingForStructuredDataPlugin( + debugger, GetGlobalProperties()->GetValueProperties(), + ConstString("Properties for the darwin-log" + " plug-in."), + is_global_setting); + } } -Error -StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info, - Target *target) -{ - Error error; - - // If we're not debugging this launched process, there's nothing for us - // to do here. - if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug)) - return error; - - // Darwin os_log() support automatically adds debug-level and info-level - // messages when a debugger is attached to a process. However, with - // integrated suppport for debugging built into the command-line LLDB, - // the user may specifically set to *not* include debug-level and info-level - // content. When the user is using the integrated log support, we want - // to put the kabosh on that automatic adding of info and debug level. - // This is done by adding an environment variable to the process on launch. - // (This also means it is not possible to suppress this behavior if - // attaching to an already-running app). - // Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); - - // If the target architecture is not one that supports DarwinLog, we have - // nothing to do here. - auto &triple = target ? target->GetArchitecture().GetTriple() : - launch_info.GetArchitecture().GetTriple(); - if (triple.getVendor() != - llvm::Triple::Apple) - { - return error; - } - - // If DarwinLog is not enabled (either by explicit user command or via - // the auto-enable option), then we have nothing to do. - if (!GetGlobalProperties()->GetEnableOnStartup() && - !s_is_explicitly_enabled) - { - // Nothing to do, DarwinLog is not enabled. - return error; - } - - // If we don't have parsed configuration info, that implies we have - // enable-on-startup set up, but we haven't yet attempted to run the - // enable command. - if (!target) - { - // We really can't do this without a target. We need to be able - // to get to the debugger to get the proper options to do this right. - // TODO log. - error.SetErrorString("requires a target to auto-enable DarwinLog."); - return error; - } - - DebuggerSP debugger_sp = target->GetDebugger().shared_from_this(); - auto options_sp = GetGlobalEnableOptions(debugger_sp); - if (!options_sp && debugger_sp) - { - options_sp = ParseAutoEnableOptions(error, *debugger_sp.get()); - if (!options_sp || !error.Success()) - return error; - - // We already parsed the options, save them now so we don't generate - // them again until the user runs the command manually. - SetGlobalEnableOptions(debugger_sp, options_sp); - } - - auto &env_vars = launch_info.GetEnvironmentEntries(); - if (!options_sp->GetEchoToStdErr()) - { - // The user doesn't want to see os_log/NSLog messages echo to stderr. - // That mechanism is entirely separate from the DarwinLog support. - // By default we don't want to get it via stderr, because that would - // be in duplicate of the explicit log support here. - - // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent - // echoing of os_log()/NSLog() to stderr in the target program. - size_t argument_index; - if (env_vars.ContainsEnvironmentVariable("OS_ACTIVITY_DT_MODE", - &argument_index)) - env_vars.DeleteArgumentAtIndex(argument_index); - - // We will also set the env var that tells any downstream launcher - // from adding OS_ACTIVITY_DT_MODE. - env_vars.AddOrReplaceEnvironmentVariable( - "IDE_DISABLED_OS_ACTIVITY_DT_MODE", "1"); - } +Error StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info, + Target *target) { + Error error; - // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable - // debug and info level messages. - const char *env_var_value; - if (options_sp->GetIncludeDebugLevel()) - env_var_value = "debug"; - else if (options_sp->GetIncludeInfoLevel()) - env_var_value = "info"; - else - env_var_value = ""; + // If we're not debugging this launched process, there's nothing for us + // to do here. + if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug)) + return error; - if (env_var_value) - { - const char *env_var_name = "OS_ACTIVITY_MODE"; - launch_info.GetEnvironmentEntries() - .AddOrReplaceEnvironmentVariable(env_var_name, env_var_value); - } + // Darwin os_log() support automatically adds debug-level and info-level + // messages when a debugger is attached to a process. However, with + // integrated suppport for debugging built into the command-line LLDB, + // the user may specifically set to *not* include debug-level and info-level + // content. When the user is using the integrated log support, we want + // to put the kabosh on that automatic adding of info and debug level. + // This is done by adding an environment variable to the process on launch. + // (This also means it is not possible to suppress this behavior if + // attaching to an already-running app). + // Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + // If the target architecture is not one that supports DarwinLog, we have + // nothing to do here. + auto &triple = target ? target->GetArchitecture().GetTriple() + : launch_info.GetArchitecture().GetTriple(); + if (triple.getVendor() != llvm::Triple::Apple) { + return error; + } + // If DarwinLog is not enabled (either by explicit user command or via + // the auto-enable option), then we have nothing to do. + if (!GetGlobalProperties()->GetEnableOnStartup() && + !s_is_explicitly_enabled) { + // Nothing to do, DarwinLog is not enabled. + return error; + } + + // If we don't have parsed configuration info, that implies we have + // enable-on-startup set up, but we haven't yet attempted to run the + // enable command. + if (!target) { + // We really can't do this without a target. We need to be able + // to get to the debugger to get the proper options to do this right. + // TODO log. + error.SetErrorString("requires a target to auto-enable DarwinLog."); return error; + } + + DebuggerSP debugger_sp = target->GetDebugger().shared_from_this(); + auto options_sp = GetGlobalEnableOptions(debugger_sp); + if (!options_sp && debugger_sp) { + options_sp = ParseAutoEnableOptions(error, *debugger_sp.get()); + if (!options_sp || !error.Success()) + return error; + + // We already parsed the options, save them now so we don't generate + // them again until the user runs the command manually. + SetGlobalEnableOptions(debugger_sp, options_sp); + } + + auto &env_vars = launch_info.GetEnvironmentEntries(); + if (!options_sp->GetEchoToStdErr()) { + // The user doesn't want to see os_log/NSLog messages echo to stderr. + // That mechanism is entirely separate from the DarwinLog support. + // By default we don't want to get it via stderr, because that would + // be in duplicate of the explicit log support here. + + // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent + // echoing of os_log()/NSLog() to stderr in the target program. + size_t argument_index; + if (env_vars.ContainsEnvironmentVariable("OS_ACTIVITY_DT_MODE", + &argument_index)) + env_vars.DeleteArgumentAtIndex(argument_index); + + // We will also set the env var that tells any downstream launcher + // from adding OS_ACTIVITY_DT_MODE. + env_vars.AddOrReplaceEnvironmentVariable("IDE_DISABLED_OS_ACTIVITY_DT_MODE", + "1"); + } + + // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable + // debug and info level messages. + const char *env_var_value; + if (options_sp->GetIncludeDebugLevel()) + env_var_value = "debug"; + else if (options_sp->GetIncludeInfoLevel()) + env_var_value = "info"; + else + env_var_value = ""; + + if (env_var_value) { + const char *env_var_name = "OS_ACTIVITY_MODE"; + launch_info.GetEnvironmentEntries().AddOrReplaceEnvironmentVariable( + env_var_name, env_var_value); + } + + return error; } -bool -StructuredDataDarwinLog::InitCompletionHookCallback(void *baton, - StoppointCallbackContext *context, - lldb::user_id_t break_id, - lldb::user_id_t break_loc_id) -{ - // We hit the init function. We now want to enqueue our new thread plan, - // which will in turn enqueue a StepOut thread plan. When the StepOut - // finishes and control returns to our new thread plan, that is the time - // when we can execute our logic to enable the logging support. - - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); +bool StructuredDataDarwinLog::InitCompletionHookCallback( + void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { + // We hit the init function. We now want to enqueue our new thread plan, + // which will in turn enqueue a StepOut thread plan. When the StepOut + // finishes and control returns to our new thread plan, that is the time + // when we can execute our logic to enable the logging support. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__); + + // Get the current thread. + if (!context) { if (log) - log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__); + log->Printf("StructuredDataDarwinLog::%s() warning: no context, " + "ignoring", + __FUNCTION__); + return false; + } - // Get the current thread. - if (!context) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: no context, " - "ignoring", __FUNCTION__); - return false; - } + // Get the plugin from the process. + auto process_sp = context->exe_ctx_ref.GetProcessSP(); + if (!process_sp) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() warning: invalid " + "process in context, ignoring", + __FUNCTION__); + return false; + } + if (log) + log->Printf("StructuredDataDarwinLog::%s() call is for process uid %d", + __FUNCTION__, process_sp->GetUniqueID()); - // Get the plugin from the process. - auto process_sp = context->exe_ctx_ref.GetProcessSP(); - if (!process_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: invalid " - "process in context, ignoring", __FUNCTION__); - return false; - } + auto plugin_sp = process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); + if (!plugin_sp) { if (log) - log->Printf("StructuredDataDarwinLog::%s() call is for process uid %d", - __FUNCTION__, process_sp->GetUniqueID()); + log->Printf("StructuredDataDarwinLog::%s() warning: no plugin for " + "feature %s in process uid %u", + __FUNCTION__, GetDarwinLogTypeName().AsCString(), + process_sp->GetUniqueID()); + return false; + } - auto plugin_sp = - process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName()); - if (!plugin_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: no plugin for " - "feature %s in process uid %u", - __FUNCTION__, GetDarwinLogTypeName().AsCString(), - process_sp->GetUniqueID()); - return false; - } + // Create the callback for when the thread plan completes. + bool called_enable_method = false; + const auto process_uid = process_sp->GetUniqueID(); - // Create the callback for when the thread plan completes. - bool called_enable_method = false; - const auto process_uid = process_sp->GetUniqueID(); - - std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp); - ThreadPlanCallOnFunctionExit::Callback callback = - [plugin_wp, &called_enable_method, log, process_uid]() { - if (log) - log->Printf("StructuredDataDarwinLog::post-init callback: " - "called (process uid %u)", process_uid); - - auto strong_plugin_sp = plugin_wp.lock(); - if (!strong_plugin_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::post-init callback: " - "plugin no longer exists, ignoring (process " - "uid %u)", process_uid); - return; - } - // Make sure we only call it once, just in case the - // thread plan hits the breakpoint twice. - if (!called_enable_method) - { - if (log) - log->Printf("StructuredDataDarwinLog::post-init callback: " - "calling EnableNow() (process uid %u)", - process_uid); - static_cast<StructuredDataDarwinLog*>(strong_plugin_sp.get()) - ->EnableNow(); - called_enable_method = true; - } - else - { - // Our breakpoint was hit more than once. Unexpected but - // no harm done. Log it. - if (log) - log->Printf("StructuredDataDarwinLog::post-init callback: " - "skipping EnableNow(), already called by " - "callback [we hit this more than once] " - "(process uid %u)", process_uid); - } - }; - - // Grab the current thread. - auto thread_sp = context->exe_ctx_ref.GetThreadSP(); - if (!thread_sp) - { + std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp); + ThreadPlanCallOnFunctionExit::Callback callback = + [plugin_wp, &called_enable_method, log, process_uid]() { if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: failed to " - "retrieve the current thread from the execution " - "context, nowhere to run the thread plan (process uid " - "%u)", __FUNCTION__, process_sp->GetUniqueID()); - return false; - } - - // Queue the thread plan. - auto thread_plan_sp - = ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp.get(), - callback)); - const bool abort_other_plans = false; - thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans); + log->Printf("StructuredDataDarwinLog::post-init callback: " + "called (process uid %u)", + process_uid); + + auto strong_plugin_sp = plugin_wp.lock(); + if (!strong_plugin_sp) { + if (log) + log->Printf("StructuredDataDarwinLog::post-init callback: " + "plugin no longer exists, ignoring (process " + "uid %u)", + process_uid); + return; + } + // Make sure we only call it once, just in case the + // thread plan hits the breakpoint twice. + if (!called_enable_method) { + if (log) + log->Printf("StructuredDataDarwinLog::post-init callback: " + "calling EnableNow() (process uid %u)", + process_uid); + static_cast<StructuredDataDarwinLog *>(strong_plugin_sp.get()) + ->EnableNow(); + called_enable_method = true; + } else { + // Our breakpoint was hit more than once. Unexpected but + // no harm done. Log it. + if (log) + log->Printf("StructuredDataDarwinLog::post-init callback: " + "skipping EnableNow(), already called by " + "callback [we hit this more than once] " + "(process uid %u)", + process_uid); + } + }; + + // Grab the current thread. + auto thread_sp = context->exe_ctx_ref.GetThreadSP(); + if (!thread_sp) { if (log) - log->Printf("StructuredDataDarwinLog::%s() queuing thread plan on " - "trace library init method entry (process uid %u)", - __FUNCTION__, process_sp->GetUniqueID()); - - // We return false here to indicate that it isn't a public stop. + log->Printf("StructuredDataDarwinLog::%s() warning: failed to " + "retrieve the current thread from the execution " + "context, nowhere to run the thread plan (process uid " + "%u)", + __FUNCTION__, process_sp->GetUniqueID()); return false; + } + + // Queue the thread plan. + auto thread_plan_sp = ThreadPlanSP( + new ThreadPlanCallOnFunctionExit(*thread_sp.get(), callback)); + const bool abort_other_plans = false; + thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans); + if (log) + log->Printf("StructuredDataDarwinLog::%s() queuing thread plan on " + "trace library init method entry (process uid %u)", + __FUNCTION__, process_sp->GetUniqueID()); + + // We return false here to indicate that it isn't a public stop. + return false; } -void -StructuredDataDarwinLog::AddInitCompletionHook(Process &process) -{ - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf("StructuredDataDarwinLog::%s() called (process uid %u)", +void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("StructuredDataDarwinLog::%s() called (process uid %u)", + __FUNCTION__, process.GetUniqueID()); + + // Make sure we haven't already done this. + { + std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex); + if (m_added_breakpoint) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() ignoring request, " + "breakpoint already set (process uid %u)", __FUNCTION__, process.GetUniqueID()); - - // Make sure we haven't already done this. - { - std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex); - if (m_added_breakpoint) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() ignoring request, " - "breakpoint already set (process uid %u)", - __FUNCTION__, process.GetUniqueID()); - return; - } - - // We're about to do this, don't let anybody else try to do it. - m_added_breakpoint = true; - } - - // Set a breakpoint for the process that will kick in when libtrace - // has finished its initialization. - Target &target = process.GetTarget(); - - // Build up the module list. - FileSpecList module_spec_list; - auto module_file_spec = - FileSpec(GetGlobalProperties()->GetLoggingModuleName(), false); - module_spec_list.Append(module_file_spec); - - // We aren't specifying a source file set. - FileSpecList *source_spec_list = nullptr; - - const char *func_name = "_libtrace_init"; - const lldb::addr_t offset = 0; - const LazyBool skip_prologue = eLazyBoolCalculate; - // This is an internal breakpoint - the user shouldn't see it. - const bool internal = true; - const bool hardware = false; - - auto breakpoint_sp = - target.CreateBreakpoint(&module_spec_list, source_spec_list, func_name, - eFunctionNameTypeFull, eLanguageTypeC, offset, - skip_prologue, internal, hardware); - if (!breakpoint_sp) - { - // Huh? Bail here. - if (log) - log->Printf("StructuredDataDarwinLog::%s() failed to set " - "breakpoint in module %s, function %s (process uid %u)", - __FUNCTION__, - GetGlobalProperties()->GetLoggingModuleName(), - func_name, process.GetUniqueID()); - return; + return; } - // Set our callback. - breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr); + // We're about to do this, don't let anybody else try to do it. + m_added_breakpoint = true; + } + + // Set a breakpoint for the process that will kick in when libtrace + // has finished its initialization. + Target &target = process.GetTarget(); + + // Build up the module list. + FileSpecList module_spec_list; + auto module_file_spec = + FileSpec(GetGlobalProperties()->GetLoggingModuleName(), false); + module_spec_list.Append(module_file_spec); + + // We aren't specifying a source file set. + FileSpecList *source_spec_list = nullptr; + + const char *func_name = "_libtrace_init"; + const lldb::addr_t offset = 0; + const LazyBool skip_prologue = eLazyBoolCalculate; + // This is an internal breakpoint - the user shouldn't see it. + const bool internal = true; + const bool hardware = false; + + auto breakpoint_sp = target.CreateBreakpoint( + &module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull, + eLanguageTypeC, offset, skip_prologue, internal, hardware); + if (!breakpoint_sp) { + // Huh? Bail here. if (log) - log->Printf("StructuredDataDarwinLog::%s() breakpoint set in module %s," - "function %s (process uid %u)", - __FUNCTION__, - GetGlobalProperties()->GetLoggingModuleName(), - func_name, process.GetUniqueID()); + log->Printf("StructuredDataDarwinLog::%s() failed to set " + "breakpoint in module %s, function %s (process uid %u)", + __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(), + func_name, process.GetUniqueID()); + return; + } + + // Set our callback. + breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr); + if (log) + log->Printf("StructuredDataDarwinLog::%s() breakpoint set in module %s," + "function %s (process uid %u)", + __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(), + func_name, process.GetUniqueID()); } -void -StructuredDataDarwinLog::DumpTimestamp(Stream &stream, uint64_t timestamp) -{ - const uint64_t delta_nanos = timestamp - m_first_timestamp_seen; +void StructuredDataDarwinLog::DumpTimestamp(Stream &stream, + uint64_t timestamp) { + const uint64_t delta_nanos = timestamp - m_first_timestamp_seen; - const uint64_t hours = delta_nanos / NANOS_PER_HOUR; - uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR; + const uint64_t hours = delta_nanos / NANOS_PER_HOUR; + uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR; - const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE; - nanos_remaining = nanos_remaining % NANOS_PER_MINUTE; + const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE; + nanos_remaining = nanos_remaining % NANOS_PER_MINUTE; - const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND; - nanos_remaining = nanos_remaining % NANOS_PER_SECOND; + const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND; + nanos_remaining = nanos_remaining % NANOS_PER_SECOND; - stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64, - hours, minutes, seconds, nanos_remaining); + stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64, hours, + minutes, seconds, nanos_remaining); } size_t StructuredDataDarwinLog::DumpHeader(Stream &output_stream, - const StructuredData::Dictionary &event) -{ - StreamString stream; - - ProcessSP process_sp = GetProcess(); - if (!process_sp) - { - // TODO log - return 0; - } - - DebuggerSP debugger_sp = - process_sp->GetTarget().GetDebugger().shared_from_this(); - if (!debugger_sp) - { - // TODO log - return 0; - } - - auto options_sp = GetGlobalEnableOptions(debugger_sp); - if (!options_sp) - { - // TODO log - return 0; + const StructuredData::Dictionary &event) { + StreamString stream; + + ProcessSP process_sp = GetProcess(); + if (!process_sp) { + // TODO log + return 0; + } + + DebuggerSP debugger_sp = + process_sp->GetTarget().GetDebugger().shared_from_this(); + if (!debugger_sp) { + // TODO log + return 0; + } + + auto options_sp = GetGlobalEnableOptions(debugger_sp); + if (!options_sp) { + // TODO log + return 0; + } + + // Check if we should even display a header. + if (!options_sp->GetDisplayAnyHeaderFields()) + return 0; + + stream.PutChar('['); + + int header_count = 0; + if (options_sp->GetDisplayTimestampRelative()) { + uint64_t timestamp = 0; + if (event.GetValueForKeyAsInteger("timestamp", timestamp)) { + DumpTimestamp(stream, timestamp); + ++header_count; } - - // Check if we should even display a header. - if (!options_sp->GetDisplayAnyHeaderFields()) - return 0; - - stream.PutChar('['); - - int header_count = 0; - if (options_sp->GetDisplayTimestampRelative()) - { - uint64_t timestamp = 0; - if (event.GetValueForKeyAsInteger("timestamp", timestamp)) - { - DumpTimestamp(stream, timestamp); - ++header_count; - } - } - - if (options_sp->GetDisplayActivityChain()) - { - std::string activity_chain; - if (event.GetValueForKeyAsString("activity-chain", activity_chain) && - !activity_chain.empty()) - { - if (header_count > 0) - stream.PutChar(','); - - // Switch over to the #if 0 branch once we figure out - // how we want to present settings for the tri-state of - // no-activity, activity (most derived only), or activity-chain. + } + + if (options_sp->GetDisplayActivityChain()) { + std::string activity_chain; + if (event.GetValueForKeyAsString("activity-chain", activity_chain) && + !activity_chain.empty()) { + if (header_count > 0) + stream.PutChar(','); + +// Switch over to the #if 0 branch once we figure out +// how we want to present settings for the tri-state of +// no-activity, activity (most derived only), or activity-chain. #if 1 - // Display the activity chain, from parent-most to child-most - // activity, separated by a colon (:). - stream.PutCString("activity-chain="); - stream.PutCString(activity_chain.c_str()); + // Display the activity chain, from parent-most to child-most + // activity, separated by a colon (:). + stream.PutCString("activity-chain="); + stream.PutCString(activity_chain.c_str()); #else - if (GetGlobalProperties()->GetDisplayActivityChain()) - { - // Display the activity chain, from parent-most to child-most - // activity, separated by a colon (:). - stream.PutCString("activity-chain="); - stream.PutCString(activity_chain.c_str()); - } - else - { - // We're only displaying the child-most activity. - stream.PutCString("activity="); - auto pos = activity_chain.find_last_of(':'); - if (pos == std::string::npos) - { - // The activity chain only has one level, use the whole - // thing. - stream.PutCString(activity_chain.c_str()); - } - else - { - // Display everything after the final ':'. - stream.PutCString(activity_chain.substr(pos+1).c_str()); - } - } + if (GetGlobalProperties()->GetDisplayActivityChain()) { + // Display the activity chain, from parent-most to child-most + // activity, separated by a colon (:). + stream.PutCString("activity-chain="); + stream.PutCString(activity_chain.c_str()); + } else { + // We're only displaying the child-most activity. + stream.PutCString("activity="); + auto pos = activity_chain.find_last_of(':'); + if (pos == std::string::npos) { + // The activity chain only has one level, use the whole + // thing. + stream.PutCString(activity_chain.c_str()); + } else { + // Display everything after the final ':'. + stream.PutCString(activity_chain.substr(pos + 1).c_str()); + } + } #endif - ++header_count; - } + ++header_count; } - - if (options_sp->GetDisplaySubsystem()) - { - std::string subsystem; - if (event.GetValueForKeyAsString("subsystem", - subsystem) && - !subsystem.empty()) - { - if (header_count > 0) - stream.PutChar(','); - stream.PutCString("subsystem="); - stream.PutCString(subsystem.c_str()); - ++header_count; - } + } + + if (options_sp->GetDisplaySubsystem()) { + std::string subsystem; + if (event.GetValueForKeyAsString("subsystem", subsystem) && + !subsystem.empty()) { + if (header_count > 0) + stream.PutChar(','); + stream.PutCString("subsystem="); + stream.PutCString(subsystem.c_str()); + ++header_count; } - - if (options_sp->GetDisplayCategory()) - { - std::string category; - if (event.GetValueForKeyAsString("category", - category) && - !category.empty()) - { - if (header_count > 0) - stream.PutChar(','); - stream.PutCString("category="); - stream.PutCString(category.c_str()); - ++header_count; - } + } + + if (options_sp->GetDisplayCategory()) { + std::string category; + if (event.GetValueForKeyAsString("category", category) && + !category.empty()) { + if (header_count > 0) + stream.PutChar(','); + stream.PutCString("category="); + stream.PutCString(category.c_str()); + ++header_count; } - stream.PutCString("] "); + } + stream.PutCString("] "); - auto &result = stream.GetString(); - output_stream.PutCString(result.c_str()); + auto &result = stream.GetString(); + output_stream.PutCString(result.c_str()); - return result.size(); + return result.size(); } -size_t -StructuredDataDarwinLog::HandleDisplayOfEvent(const StructuredData::Dictionary - &event, Stream &stream) -{ - // Check the type of the event. - ConstString event_type; - if (!event.GetValueForKeyAsString("type", event_type)) - { - // Hmm, we expected to get events that describe - // what they are. Continue anyway. - return 0; - } +size_t StructuredDataDarwinLog::HandleDisplayOfEvent( + const StructuredData::Dictionary &event, Stream &stream) { + // Check the type of the event. + ConstString event_type; + if (!event.GetValueForKeyAsString("type", event_type)) { + // Hmm, we expected to get events that describe + // what they are. Continue anyway. + return 0; + } - if (event_type != GetLogEventType()) - return 0; + if (event_type != GetLogEventType()) + return 0; - size_t total_bytes = 0; + size_t total_bytes = 0; - // Grab the message content. - std::string message; - if (!event.GetValueForKeyAsString("message", message)) - return true; + // Grab the message content. + std::string message; + if (!event.GetValueForKeyAsString("message", message)) + return true; - // Display the log entry. - const auto len = message.length(); + // Display the log entry. + const auto len = message.length(); - total_bytes += DumpHeader(stream, event); + total_bytes += DumpHeader(stream, event); - stream.Write(message.c_str(), len); - total_bytes += len; + stream.Write(message.c_str(), len); + total_bytes += len; - // Add an end of line. - stream.PutChar('\n'); - total_bytes += sizeof(char); + // Add an end of line. + stream.PutChar('\n'); + total_bytes += sizeof(char); - return total_bytes; + return total_bytes; } -void -StructuredDataDarwinLog::EnableNow() -{ - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__); +void StructuredDataDarwinLog::EnableNow() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__); - // Run the enable command. - auto process_sp = GetProcess(); - if (!process_sp) - { - // Nothing to do. - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: failed to get " - "valid process, skipping", __FUNCTION__); - return; - } + // Run the enable command. + auto process_sp = GetProcess(); + if (!process_sp) { + // Nothing to do. if (log) - log->Printf("StructuredDataDarwinLog::%s() call is for process uid %u", + log->Printf("StructuredDataDarwinLog::%s() warning: failed to get " + "valid process, skipping", + __FUNCTION__); + return; + } + if (log) + log->Printf("StructuredDataDarwinLog::%s() call is for process uid %u", + __FUNCTION__, process_sp->GetUniqueID()); + + // If we have configuration data, we can directly enable it now. + // Otherwise, we need to run through the command interpreter to parse + // the auto-run options (which is the only way we get here without having + // already-parsed configuration data). + DebuggerSP debugger_sp = + process_sp->GetTarget().GetDebugger().shared_from_this(); + if (!debugger_sp) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() warning: failed to get " + "debugger shared pointer, skipping (process uid %u)", + __FUNCTION__, process_sp->GetUniqueID()); + return; + } + + auto options_sp = GetGlobalEnableOptions(debugger_sp); + if (!options_sp) { + // We haven't run the enable command yet. Just do that now, it'll + // take care of the rest. + auto &interpreter = debugger_sp->GetCommandInterpreter(); + const bool success = RunEnableCommand(interpreter); + if (log) { + if (success) + log->Printf("StructuredDataDarwinLog::%s() ran enable command " + "successfully for (process uid %u)", + __FUNCTION__, process_sp->GetUniqueID()); + else + log->Printf("StructuredDataDarwinLog::%s() error: running " + "enable command failed (process uid %u)", __FUNCTION__, process_sp->GetUniqueID()); - - // If we have configuration data, we can directly enable it now. - // Otherwise, we need to run through the command interpreter to parse - // the auto-run options (which is the only way we get here without having - // already-parsed configuration data). - DebuggerSP debugger_sp = - process_sp->GetTarget().GetDebugger().shared_from_this(); - if (!debugger_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: failed to get " - "debugger shared pointer, skipping (process uid %u)", - __FUNCTION__, process_sp->GetUniqueID()); - return; - } - - auto options_sp = GetGlobalEnableOptions(debugger_sp); - if (!options_sp) - { - // We haven't run the enable command yet. Just do that now, it'll - // take care of the rest. - auto &interpreter = debugger_sp->GetCommandInterpreter(); - const bool success = RunEnableCommand(interpreter); - if (log) - { - if (success) - log->Printf("StructuredDataDarwinLog::%s() ran enable command " - "successfully for (process uid %u)", - __FUNCTION__, process_sp->GetUniqueID()); - else - log->Printf("StructuredDataDarwinLog::%s() error: running " - "enable command failed (process uid %u)", - __FUNCTION__, process_sp->GetUniqueID()); - } - // Report failures to the debugger error stream. - auto error_stream_sp = debugger_sp->GetAsyncErrorStream(); - if (error_stream_sp) - { - error_stream_sp->Printf("failed to configure DarwinLog " - "support\n"); - error_stream_sp->Flush(); - } - return; } - - // We've previously been enabled. We will re-enable now with the - // previously specified options. - auto config_sp = options_sp->BuildConfigurationData(true); - if (!config_sp) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() warning: failed to " - "build configuration data for enable options, skipping " - "(process uid %u)", __FUNCTION__, - process_sp->GetUniqueID()); - return; + // Report failures to the debugger error stream. + auto error_stream_sp = debugger_sp->GetAsyncErrorStream(); + if (error_stream_sp) { + error_stream_sp->Printf("failed to configure DarwinLog " + "support\n"); + error_stream_sp->Flush(); } + return; + } - // We can run it directly. - // Send configuration to the feature by way of the process. - const Error error = - process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), - config_sp); - - // Report results. - if (!error.Success()) - { - if (log) - log->Printf("StructuredDataDarwinLog::%s() " - "ConfigureStructuredData() call failed " - "(process uid %u): %s", - __FUNCTION__, process_sp->GetUniqueID(), - error.AsCString()); - auto error_stream_sp = debugger_sp->GetAsyncErrorStream(); - if (error_stream_sp) - { - error_stream_sp->Printf("failed to configure DarwinLog " - "support: %s\n", error.AsCString()); - error_stream_sp->Flush(); - } - m_is_enabled = false; - } - else - { - m_is_enabled = true; - if (log) - log->Printf("StructuredDataDarwinLog::%s() success via direct " - "configuration (process uid %u)", __FUNCTION__, - process_sp->GetUniqueID()); + // We've previously been enabled. We will re-enable now with the + // previously specified options. + auto config_sp = options_sp->BuildConfigurationData(true); + if (!config_sp) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() warning: failed to " + "build configuration data for enable options, skipping " + "(process uid %u)", + __FUNCTION__, process_sp->GetUniqueID()); + return; + } + + // We can run it directly. + // Send configuration to the feature by way of the process. + const Error error = + process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp); + + // Report results. + if (!error.Success()) { + if (log) + log->Printf("StructuredDataDarwinLog::%s() " + "ConfigureStructuredData() call failed " + "(process uid %u): %s", + __FUNCTION__, process_sp->GetUniqueID(), error.AsCString()); + auto error_stream_sp = debugger_sp->GetAsyncErrorStream(); + if (error_stream_sp) { + error_stream_sp->Printf("failed to configure DarwinLog " + "support: %s\n", + error.AsCString()); + error_stream_sp->Flush(); } + m_is_enabled = false; + } else { + m_is_enabled = true; + if (log) + log->Printf("StructuredDataDarwinLog::%s() success via direct " + "configuration (process uid %u)", + __FUNCTION__, process_sp->GetUniqueID()); + } } |