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