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