From 705b1809641ba3e102d437cffff29bfe2b611cab Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 13 Jun 2014 02:37:02 +0000 Subject: [PATCH] Initial merge of some of the iOS 8 / Mac OS X Yosemite specific lldb support. I'll be doing more testing & cleanup but I wanted to get the initial checkin done. This adds a new SBExpressionOptions::SetLanguage API for selecting a language of an expression. I added adds a new SBThread::GetInfoItemByPathString for retriving information about a thread from that thread's StructuredData. I added a new StructuredData class for representing key-value/array/dictionary information (e.g. JSON formatted data). Helper functions to read JSON and create a StructuredData object, and to print a StructuredData object in JSON format are included. A few Cocoa / Cocoa Touch data formatters were updated by Enrico to track changes in iOS 8 / Yosemite. Before we query a thread's extended information, the system runtime may provide hints to the remote debug stub that it will use to retrieve values out of runtime structures. I added a new SystemRuntime method AddThreadExtendedInfoPacketHints which allows the SystemRuntime to add key-value type data to the initial request that we send to the remote stub. The thread-format formatter string can now retrieve values out of a thread's extended info structured data. The default thread-format string picks up two of these - thread.info.activity.name and thread.info.trace_messages. I added a new "jThreadExtendedInfo" packet in debugserver; I will add documentation to the lldb-gdb-remote.txt doc soon. It accepts JSON formatted arguments (most importantly, "thread":threadnum) and it returns a variety of information regarding the thread to lldb in JSON format. This JSON return is scanned into a StructuredData object that is associated with the thread; UI layers can query the thread's StructuredData to see if key-values are present, and if so, show them to the user. These key-values are likely to be specific to different targets with some commonality among many targets. For instance, many targets will be able to advertise the pthread_t value for a thread. I added an initial rough cut of "thread info" command which will print the information about a thread from the jThreadExtendedInfo result. I need to do more work to make this format reasonably. Han Ming added calls into the pmenergy and pmsample libraries if debugserver is run on Mac OS X Yosemite to get information about the inferior's power use. I added support to debugserver for gathering the Genealogy information about threads, if it exists, and returning it in the jThreadExtendedInfo JSON result. llvm-svn: 210874 --- lldb/include/lldb/API/SBExpressionOptions.h | 3 + lldb/include/lldb/API/SBThread.h | 3 + lldb/include/lldb/Core/StructuredData.h | 486 ++++++++++++++++++ .../DataFormatters/CXXFormatterFunctions.h | 111 +--- lldb/include/lldb/Target/SystemRuntime.h | 15 + lldb/include/lldb/Target/Thread.h | 35 ++ lldb/lldb.xcodeproj/project.pbxproj | 4 + .../Python/interface/SBExpressionOptions.i | 4 + lldb/scripts/Python/interface/SBThread.i | 11 + lldb/source/API/SBExpressionOptions.cpp | 6 + lldb/source/API/SBThread.cpp | 69 +++ lldb/source/Commands/CommandObjectThread.cpp | 188 +++++++ lldb/source/Core/CMakeLists.txt | 1 + lldb/source/Core/Debugger.cpp | 103 ++++ lldb/source/Core/StructuredData.cpp | 427 +++++++++++++++ .../DataFormatters/CXXFormatterFunctions.cpp | 62 ++- lldb/source/DataFormatters/NSArray.cpp | 378 +++++++++++++- .../GDBRemoteCommunicationClient.cpp | 19 + .../gdb-remote/GDBRemoteCommunicationClient.h | 4 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 45 ++ .../Process/gdb-remote/ProcessGDBRemote.h | 4 + .../Process/gdb-remote/ThreadGDBRemote.cpp | 17 + .../Process/gdb-remote/ThreadGDBRemote.h | 4 + .../MacOSX/SystemRuntimeMacOSX.cpp | 180 ++++++- .../MacOSX/SystemRuntimeMacOSX.h | 95 +++- lldb/source/Target/Thread.cpp | 83 ++- .../debugserver.xcodeproj/project.pbxproj | 113 ++-- lldb/tools/debugserver/source/DNB.cpp | 69 +++ lldb/tools/debugserver/source/DNB.h | 9 + lldb/tools/debugserver/source/DNBDefs.h | 2 + .../debugserver/source/MacOSX/CMakeLists.txt | 1 + .../debugserver/source/MacOSX/Genealogy.cpp | 302 +++++++++++ .../debugserver/source/MacOSX/Genealogy.h | 116 +++++ .../debugserver/source/MacOSX/GenealogySPI.h | 96 ++++ .../debugserver/source/MacOSX/MachProcess.h | 12 + .../debugserver/source/MacOSX/MachProcess.mm | 41 ++ .../debugserver/source/MacOSX/MachTask.mm | 54 +- .../debugserver/source/MacOSX/MachThread.cpp | 205 +++++++- .../debugserver/source/MacOSX/MachThread.h | 13 +- .../source/MacOSX/MachThreadList.cpp | 48 +- .../source/MacOSX/MachThreadList.h | 8 + .../source/MacOSX/MachVMMemory.cpp | 114 ---- .../debugserver/source/MacOSX/ThreadInfo.h | 26 + lldb/tools/debugserver/source/RNBRemote.cpp | 334 ++++++++++++ lldb/tools/debugserver/source/RNBRemote.h | 2 + .../source/debugserver-entitlements.plist | 6 + .../debugserver-macosx-entitlements.plist | 8 + 47 files changed, 3648 insertions(+), 288 deletions(-) create mode 100644 lldb/include/lldb/Core/StructuredData.h create mode 100644 lldb/source/Core/StructuredData.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/Genealogy.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/Genealogy.h create mode 100644 lldb/tools/debugserver/source/MacOSX/GenealogySPI.h create mode 100644 lldb/tools/debugserver/source/MacOSX/ThreadInfo.h create mode 100644 lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h index 91c3aaaab1e7..ef46c7fed823 100644 --- a/lldb/include/lldb/API/SBExpressionOptions.h +++ b/lldb/include/lldb/API/SBExpressionOptions.h @@ -87,6 +87,9 @@ public: void SetTrapExceptions (bool trap_exceptions = true); + void + SetLanguage (lldb::LanguageType language); + void SetCancelCallback (lldb::ExpressionCancelCallback callback, void *baton); diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h index c71b55a52b15..a56e82f37224 100644 --- a/lldb/include/lldb/API/SBThread.h +++ b/lldb/include/lldb/API/SBThread.h @@ -100,6 +100,9 @@ public: lldb::queue_id_t GetQueueID() const; + bool + GetInfoItemByPathAsString ( const char *path, SBStream &strm); + void StepOver (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); diff --git a/lldb/include/lldb/Core/StructuredData.h b/lldb/include/lldb/Core/StructuredData.h new file mode 100644 index 000000000000..a4cabf4fe352 --- /dev/null +++ b/lldb/include/lldb/Core/StructuredData.h @@ -0,0 +1,486 @@ +//===-- StructuredData.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StructuredData_h_ +#define liblldb_StructuredData_h_ + +// C Includes +// C++ Includes + +#include +#include +#include +#include + +#include "llvm/ADT/StringRef.h" + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class StructuredData StructuredData.h "lldb/Core/StructuredData.h" +/// @brief A class which can hold structured data +/// +/// The StructuredData class is designed to hold the data from a JSON +/// or plist style file -- a serialized data structure with dictionaries +/// (maps, hashes), arrays, and concrete values like integers, floating +/// point numbers, strings, booleans. +/// +/// StructuredData does not presuppose any knowledge of the schema for +/// the data it is holding; it can parse JSON data, for instance, and +/// other parts of lldb can iterate through the parsed data set to find +/// keys and values that may be present. +//---------------------------------------------------------------------- + +class StructuredData +{ +public: + + class Object; + class Array; + class Integer; + class Float; + class Boolean; + class String; + class Dictionary; + + typedef std::shared_ptr ObjectSP; + typedef std::shared_ptr ArraySP; + typedef std::shared_ptr DictionarySP; + + enum class Type { + eTypeInvalid = -1, + eTypeNull = 0, + eTypeArray, + eTypeInteger, + eTypeFloat, + eTypeBoolean, + eTypeString, + eTypeDictionary + }; + + class Object : + public std::enable_shared_from_this + { + public: + + Object (Type t = Type::eTypeInvalid) : + m_type (t) + { + } + + virtual ~Object () + { + } + + virtual void + Clear () + { + m_type = Type::eTypeInvalid; + } + + Type + GetType () const + { + return m_type; + } + + void + SetType (Type t) + { + m_type = t; + } + + Array * + GetAsArray () + { + if (m_type == Type::eTypeArray) + return (Array *)this; + return NULL; + } + + Dictionary * + GetAsDictionary () + { + if (m_type == Type::eTypeDictionary) + return (Dictionary *)this; + return NULL; + } + + Integer * + GetAsInteger () + { + if (m_type == Type::eTypeInteger) + return (Integer *)this; + return NULL; + } + + Float * + GetAsFloat () + { + if (m_type == Type::eTypeFloat) + return (Float *)this; + return NULL; + } + + Boolean * + GetAsBoolean () + { + if (m_type == Type::eTypeBoolean) + return (Boolean *)this; + return NULL; + } + + String * + GetAsString () + { + if (m_type == Type::eTypeString) + return (String *)this; + return NULL; + } + + ObjectSP + GetObjectForDotSeparatedPath (llvm::StringRef path); + + virtual void + Dump (Stream &s) const = 0; + + private: + Type m_type; + }; + + class Array : public Object + { + public: + Array () : + Object (Type::eTypeArray) + { + } + + virtual + ~Array() + { + } + + size_t + GetSize() + { + return m_items.size(); + } + + ObjectSP + operator[](size_t idx) + { + if (idx < m_items.size()) + return m_items[idx]; + return ObjectSP(); + } + + ObjectSP + GetItemAtIndex (size_t idx) + { + if (idx < m_items.size()) + return m_items[idx]; + return ObjectSP(); + } + + void + Push(ObjectSP item) + { + m_items.push_back(item); + } + + void + AddItem(ObjectSP item) + { + m_items.push_back(item); + } + + virtual void + Dump (Stream &s) const; + + protected: + typedef std::vector collection; + collection m_items; + }; + + + class Integer : public Object + { + public: + Integer () : + Object (Type::eTypeInteger), + m_value () + { + } + + virtual ~Integer() + { + } + + void + SetValue (uint64_t value) + { + m_value = value; + } + + uint64_t + GetValue () + { + return m_value; + } + + virtual void + Dump (Stream &s) const; + + protected: + uint64_t m_value; + }; + + class Float : public Object + { + public: + Float () : + Object (Type::eTypeFloat), + m_value () + { + } + + virtual ~Float() + { + } + + void + SetValue (double value) + { + m_value = value; + } + + double + GetValue () + { + return m_value; + } + + virtual void + Dump (Stream &s) const; + + protected: + double m_value; + }; + + class Boolean : public Object + { + public: + Boolean () : + Object (Type::eTypeBoolean), + m_value () + { + } + + virtual ~Boolean() + { + } + + void + SetValue (bool value) + { + m_value = value; + } + + bool + GetValue () + { + return m_value; + } + + virtual void + Dump (Stream &s) const; + + protected: + bool m_value; + }; + + + + class String : public Object + { + public: + String () : + Object (Type::eTypeString), + m_value () + { + } + + void + SetValue (std::string string) + { + m_value = string; + } + + std::string + GetValue () + { + return m_value; + } + + virtual void + Dump (Stream &s) const; + + protected: + std::string m_value; + }; + + class Dictionary : public Object + { + public: + Dictionary () : + Object (Type::eTypeDictionary), + m_dict () + { + } + + virtual ~Dictionary() + { + } + size_t + GetSize() + { + return m_dict.size(); + } + + ObjectSP + GetKeys() + { + ObjectSP object_sp(new Array ()); + Array *array = object_sp->GetAsArray(); + collection::const_iterator iter; + for (iter = m_dict.begin(); iter != m_dict.end(); ++iter) + { + ObjectSP key_object_sp(new String()); + key_object_sp->GetAsString()->SetValue(iter->first.AsCString()); + array->Push(key_object_sp); + } + return object_sp; + } + + ObjectSP + GetValueForKey (const char *key) + { + ObjectSP value_sp; + if (key) + { + ConstString key_cs(key); + for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter) + { + if (key_cs == iter->first) + { + value_sp = iter->second; + break; + } + } + } + return value_sp; + } + + bool + HasKey (const char *key) + { + ConstString key_cs (key); + collection::const_iterator search = m_dict.find(key_cs); + if (search != m_dict.end()) + { + return true; + } + else + { + return false; + } + } + + void + AddItem (const char *key, ObjectSP value) + { + ConstString key_cs(key); + m_dict[key_cs] = value; + } + + void + AddIntegerItem (const char *key, uint64_t value) + { + ObjectSP val_obj (new Integer()); + val_obj->GetAsInteger()->SetValue (value); + AddItem (key, val_obj); + } + + void + AddFloatItem (const char *key, double value) + { + ObjectSP val_obj (new Float()); + val_obj->GetAsFloat()->SetValue (value); + AddItem (key, val_obj); + } + + void + AddStringItem (const char *key, std::string value) + { + ObjectSP val_obj (new String()); + val_obj->GetAsString()->SetValue (value); + AddItem (key, val_obj); + } + + void + AddBooleanItem (const char *key, bool value) + { + ObjectSP val_obj (new Boolean()); + val_obj->GetAsBoolean()->SetValue (value); + AddItem (key, val_obj); + } + + virtual void + Dump (Stream &s) const; + + protected: + typedef std::map collection; + collection m_dict; + }; + + class Null : public Object + { + public: + Null () : + Object (Type::eTypeNull) + { + } + + virtual ~Null() + { + } + + virtual void + Dump (Stream &s) const; + + protected: + }; + + + static ObjectSP + ParseJSON (std::string json_text); + +}; // class StructuredData + + +} // namespace lldb_private + +#endif // liblldb_StructuredData_h_ diff --git a/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h b/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h index c53ef9589eea..e1f7eb1d7c1b 100644 --- a/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h +++ b/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h @@ -19,6 +19,7 @@ #include "lldb/DataFormatters/FormatClasses.h" #include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Target.h" #include "clang/AST/ASTContext.h" @@ -139,6 +140,9 @@ namespace lldb_private { bool NSStringSummaryProvider (ValueObject& valobj, Stream& stream); + bool + NSTaggedString_SummaryProvider (ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream& stream); + bool NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream); @@ -176,113 +180,6 @@ namespace lldb_private { extern template bool ObjCSELSummaryProvider (ValueObject&, Stream&); - class NSArrayMSyntheticFrontEnd : public SyntheticChildrenFrontEnd - { - private: - struct DataDescriptor_32 - { - uint32_t _used; - uint32_t _priv1 : 2 ; - uint32_t _size : 30; - uint32_t _priv2 : 2; - uint32_t offset : 30; - uint32_t _priv3; - uint32_t _data; - }; - struct DataDescriptor_64 - { - uint64_t _used; - uint64_t _priv1 : 2 ; - uint64_t _size : 62; - uint64_t _priv2 : 2; - uint64_t offset : 62; - uint32_t _priv3; - uint64_t _data; - }; - public: - NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); - - virtual size_t - CalculateNumChildren (); - - virtual lldb::ValueObjectSP - GetChildAtIndex (size_t idx); - - virtual bool - Update(); - - virtual bool - MightHaveChildren (); - - virtual size_t - GetIndexOfChildWithName (const ConstString &name); - - virtual - ~NSArrayMSyntheticFrontEnd (); - private: - ExecutionContextRef m_exe_ctx_ref; - uint8_t m_ptr_size; - DataDescriptor_32 *m_data_32; - DataDescriptor_64 *m_data_64; - ClangASTType m_id_type; - std::vector m_children; - }; - - class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd - { - public: - NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); - - virtual size_t - CalculateNumChildren (); - - virtual lldb::ValueObjectSP - GetChildAtIndex (size_t idx); - - virtual bool - Update(); - - virtual bool - MightHaveChildren (); - - virtual size_t - GetIndexOfChildWithName (const ConstString &name); - - virtual - ~NSArrayISyntheticFrontEnd (); - private: - ExecutionContextRef m_exe_ctx_ref; - uint8_t m_ptr_size; - uint64_t m_items; - lldb::addr_t m_data_ptr; - ClangASTType m_id_type; - std::vector m_children; - }; - - class NSArrayCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd - { - public: - NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); - - virtual size_t - CalculateNumChildren (); - - virtual lldb::ValueObjectSP - GetChildAtIndex (size_t idx); - - virtual bool - Update(); - - virtual bool - MightHaveChildren (); - - virtual size_t - GetIndexOfChildWithName (const ConstString &name); - - virtual - ~NSArrayCodeRunningSyntheticFrontEnd (); - }; - SyntheticChildrenFrontEnd* NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd diff --git a/lldb/include/lldb/Target/SystemRuntime.h b/lldb/include/lldb/Target/SystemRuntime.h index 7c6383491f64..18f38f79bdbd 100644 --- a/lldb/include/lldb/Target/SystemRuntime.h +++ b/lldb/include/lldb/Target/SystemRuntime.h @@ -20,6 +20,7 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/QueueItem.h" #include "lldb/lldb-private.h" @@ -310,6 +311,20 @@ public: } //------------------------------------------------------------------ + /// Add key-value pairs to the StructuredData dictionary object with + /// information debugserver may need when constructing the jThreadExtendedInfo + /// packet. + /// + /// @param [out] dict + /// Dictionary to which key-value pairs should be added; they will + /// be sent to the remote gdb server stub as arguments in the + /// jThreadExtendedInfo request. + //------------------------------------------------------------------ + virtual void + AddThreadExtendedInfoPacketHints (lldb_private::StructuredData::ObjectSP dict) + { + } + /// Determine whether it is safe to run an expression on a given thread /// /// If a system must not run functions on a thread in some particular state, diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index dfd8390b7109..582db1c3e522 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -14,6 +14,7 @@ #include "lldb/Host/Mutex.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Event.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/UserID.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Target/ExecutionContextScope.h" @@ -306,6 +307,28 @@ public: return NULL; } + //------------------------------------------------------------------ + /// Retrieve a dictionary of information about this thread + /// + /// On Mac OS X systems there may be voucher information. + /// The top level dictionary returned will have an "activity" key and the + /// value of the activity is a dictionary. Keys in that dictionary will + /// be "name" and "id", among others. + /// There may also be "trace_messages" (an array) with each entry in that array + /// being a dictionary (keys include "message" with the text of the trace + /// message). + //------------------------------------------------------------------ + StructuredData::ObjectSP + GetExtendedInfo () + { + if (m_extended_info_fetched == false) + { + m_extended_info = FetchThreadExtendedInfo (); + m_extended_info_fetched = true; + } + return m_extended_info; + } + virtual const char * GetName () { @@ -511,6 +534,9 @@ public: void DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx); + bool + GetDescription (Stream &s, lldb::DescriptionLevel level, bool json_output); + //------------------------------------------------------------------ /// Default implementation for stepping into. /// @@ -1238,6 +1264,13 @@ protected: return false; } + // Subclasses that have a way to get an extended info dictionary for this thread should + // fill + virtual lldb_private::StructuredData::ObjectSP + FetchThreadExtendedInfo () + { + return StructuredData::ObjectSP(); + } lldb::StackFrameListSP GetStackFrameList (); @@ -1268,6 +1301,8 @@ protected: bool m_destroy_called; // This is used internally to make sure derived Thread classes call DestroyThread. LazyBool m_override_should_notify; private: + bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info for this thread? + StructuredData::ObjectSP m_extended_info; // The extended info for this thread //------------------------------------------------------------------ // For Thread only //------------------------------------------------------------------ diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index 2ed69a4580ea..9ec428e93431 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -698,6 +698,7 @@ AF9107EF168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9107EC168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp */; }; AF9B8F33182DB52900DA866F /* SystemRuntimeMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */; }; AF9B8F34182DB52900DA866F /* SystemRuntimeMacOSX.h in Headers */ = {isa = PBXBuildFile; fileRef = AF9B8F32182DB52900DA866F /* SystemRuntimeMacOSX.h */; }; + AFEC3362194A8ABA00FF05C6 /* StructuredData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */; }; AFF87C87150FF669000E1742 /* com.apple.debugserver.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */; }; AFF87C89150FF672000E1742 /* com.apple.debugserver-secure.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C88150FF672000E1742 /* com.apple.debugserver-secure.plist */; }; AFF87C8F150FF688000E1742 /* com.apple.debugserver.applist.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C8E150FF688000E1742 /* com.apple.debugserver.applist.plist */; }; @@ -1965,6 +1966,7 @@ AF94005711C03F6500085DB9 /* SymbolVendor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolVendor.cpp; path = source/Symbol/SymbolVendor.cpp; sourceTree = ""; }; AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemRuntimeMacOSX.cpp; sourceTree = ""; }; AF9B8F32182DB52900DA866F /* SystemRuntimeMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemRuntimeMacOSX.h; sourceTree = ""; }; + AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StructuredData.cpp; path = source/Core/StructuredData.cpp; sourceTree = ""; }; AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.plist; path = tools/debugserver/source/com.apple.debugserver.plist; sourceTree = ""; }; AFF87C88150FF672000E1742 /* com.apple.debugserver-secure.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "com.apple.debugserver-secure.plist"; path = "tools/debugserver/source/com.apple.debugserver-secure.plist"; sourceTree = ""; }; AFF87C8A150FF677000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = ""; }; @@ -3028,6 +3030,7 @@ 4C626533130F1B0A00C889F6 /* StreamTee.h */, 9A35765E116E76A700E8ED2F /* StringList.h */, 9A35765F116E76B900E8ED2F /* StringList.cpp */, + AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */, 26B167A41123BF5500DC7B4F /* ThreadSafeValue.h */, 263FEDA5112CC1DA00E4C208 /* ThreadSafeSTLMap.h */, 26BC7D7E10F1B77400F91463 /* Timer.h */, @@ -4524,6 +4527,7 @@ 2689001413353DDE00698AC0 /* CommandObjectBreakpoint.cpp in Sources */, 2689001513353DDE00698AC0 /* CommandObjectBreakpointCommand.cpp in Sources */, 2689001613353DDE00698AC0 /* CommandObjectCommands.cpp in Sources */, + AFEC3362194A8ABA00FF05C6 /* StructuredData.cpp in Sources */, 26474CAC18D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.cpp in Sources */, 2689001713353DDE00698AC0 /* CommandObjectDisassemble.cpp in Sources */, 2689001813353DDE00698AC0 /* CommandObjectExpression.cpp in Sources */, diff --git a/lldb/scripts/Python/interface/SBExpressionOptions.i b/lldb/scripts/Python/interface/SBExpressionOptions.i index 46f464f2c58c..525b6a5ce79e 100644 --- a/lldb/scripts/Python/interface/SBExpressionOptions.i +++ b/lldb/scripts/Python/interface/SBExpressionOptions.i @@ -92,6 +92,10 @@ public: void SetTrapExceptions (bool trap_exceptions = true); + %feature ("docstring", "Sets the language that LLDB should assume the expression is written in") SetLanguage; + void + SetLanguage (lldb::LanguageType language); + protected: SBExpressionOptions (lldb_private::EvaluateExpressionOptions &expression_options); diff --git a/lldb/scripts/Python/interface/SBThread.i b/lldb/scripts/Python/interface/SBThread.i index d1dd82a0cdaa..0371f7b452bd 100644 --- a/lldb/scripts/Python/interface/SBThread.i +++ b/lldb/scripts/Python/interface/SBThread.i @@ -165,6 +165,17 @@ public: lldb::queue_id_t GetQueueID() const; + %feature("autodoc", " + Takes a path string and a SBStream reference as parameters, returns a bool. + Collects the thread's 'info' dictionary from the remote system, uses the path + argument to descend into the dictionary to an item of interest, and prints + it into the SBStream in a natural format. Return bool is to indicate if + anything was printed into the stream (true) or not (false). + ") GetInfoItemByPathAsString; + + bool + GetInfoItemByPathAsString (const char *path, lldb::SBStream &strm); + %feature("autodoc", " Return the SBQueue for this thread. If this thread is not currently associated with a libdispatch queue, the SBQueue object's IsValid() method will return false. diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp index 0c26a45e0e7e..7fbff38c1466 100644 --- a/lldb/source/API/SBExpressionOptions.cpp +++ b/lldb/source/API/SBExpressionOptions.cpp @@ -149,6 +149,12 @@ SBExpressionOptions::SetTrapExceptions (bool trap_exceptions) m_opaque_ap->SetTrapExceptions (trap_exceptions); } +void +SBExpressionOptions::SetLanguage (lldb::LanguageType language) +{ + m_opaque_ap->SetLanguage(language); +} + void SBExpressionOptions::SetCancelCallback (lldb::ExpressionCancelCallback callback, void *baton) { diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 60731e85d0b3..a0bfa4313535 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -19,6 +19,7 @@ #include "lldb/Core/State.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Thread.h" @@ -589,6 +590,74 @@ SBThread::GetQueueID () const return id; } +bool +SBThread::GetInfoItemByPathAsString (const char *path, SBStream &strm) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool success = false; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + Thread *thread = exe_ctx.GetThreadPtr(); + StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo(); + if (info_root_sp) + { + StructuredData::ObjectSP node = info_root_sp->GetObjectForDotSeparatedPath (path); + if (node) + { + if (node->GetType() == StructuredData::Type::eTypeString) + { + strm.Printf ("%s", node->GetAsString()->GetValue().c_str()); + success = true; + } + if (node->GetType() == StructuredData::Type::eTypeInteger) + { + strm.Printf ("0x%" PRIx64, node->GetAsInteger()->GetValue()); + success = true; + } + if (node->GetType() == StructuredData::Type::eTypeFloat) + { + strm.Printf ("0x%f", node->GetAsFloat()->GetValue()); + success = true; + } + if (node->GetType() == StructuredData::Type::eTypeBoolean) + { + if (node->GetAsBoolean()->GetValue() == true) + strm.Printf ("true"); + else + strm.Printf ("false"); + success = true; + } + if (node->GetType() == StructuredData::Type::eTypeNull) + { + strm.Printf ("null"); + success = true; + } + } + } + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetInfoItemByPathAsString() => error: process is running", + static_cast(exe_ctx.GetThreadPtr())); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetInfoItemByPathAsString () => %s", + static_cast(exe_ctx.GetThreadPtr()), + strm.GetData()); + + return success; +} + + SBError SBThread::ResumeNewPlan (ExecutionContext &exe_ctx, ThreadPlan *new_plan) { diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index ebf42b15782c..5dbdb164c221 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1331,6 +1331,193 @@ protected: } }; +//------------------------------------------------------------------------- +// CommandObjectThreadInfo +//------------------------------------------------------------------------- + +class CommandObjectThreadInfo : public CommandObjectParsed +{ +public: + + CommandObjectThreadInfo (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread info", + "Show an extended summary of information about thread(s) in a process.", + "thread info", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData thread_idx_arg; + + thread_idx_arg.arg_type = eArgTypeThreadIndex; + thread_idx_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (thread_idx_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + void + OptionParsingStarting () + { + m_json = false; + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + const int short_option = m_getopt_table[option_idx].val; + Error error; + + switch (short_option) + { + case 'j': + m_json = true; + break; + + default: + return Error("invalid short option character '%c'", short_option); + + } + return error; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + bool m_json; + + static OptionDefinition g_option_table[]; + }; + + virtual + Options * + GetOptions () + { + return &m_options; + } + + + virtual + ~CommandObjectThreadInfo () + { + } + + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + Stream &strm = result.GetOutputStream(); + + if (command.GetArgumentCount() == 0) + { + Thread *thread = m_exe_ctx.GetThreadPtr(); + if (thread->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) + { + Process *process = m_exe_ctx.GetProcessPtr(); + uint32_t idx = 0; + for (ThreadSP thread_sp : process->Threads()) + { + if (idx != 0) + result.AppendMessage(""); + if (!thread_sp->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) + { + result.AppendErrorWithFormat ("error displaying info for thread: \"0x%4.4x\"\n", idx); + result.SetStatus (eReturnStatusFailed); + return false; + } + ++idx; + } + } + else + { + const size_t num_args = command.GetArgumentCount(); + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + std::vector thread_sps; + + for (size_t i = 0; i < num_args; i++) + { + bool success; + + uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); + + if (!thread_sps[i]) + { + result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + } + + for (uint32_t i = 0; i < num_args; i++) + { + if (!thread_sps[i]->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) + { + result.AppendErrorWithFormat ("error displaying info for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (i < num_args - 1) + result.AppendMessage(""); + } + + } + return result.Succeeded(); + } + + CommandOptions m_options; + +}; + +OptionDefinition +CommandObjectThreadInfo::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "json",'j', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Display the thread info in JSON format."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + //------------------------------------------------------------------------- // CommandObjectThreadReturn //------------------------------------------------------------------------- @@ -1764,6 +1951,7 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter & LoadSubCommand ("jump", CommandObjectSP (new CommandObjectThreadJump (interpreter))); LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter))); LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectThreadInfo (interpreter))); LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( interpreter, "thread step-in", diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index 16730ac46b25..709433f96ff3 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -52,6 +52,7 @@ add_lldb_library(lldbCore StreamGDBRemote.cpp StreamString.cpp StringList.cpp + StructuredData.cpp Timer.cpp UserID.cpp UserSettingsController.cpp diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 4a159145c6ec..bb982f3e4db2 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" +#include "llvm/ADT/StringRef.h" #include "lldb/lldb-private.h" #include "lldb/Core/ConnectionFileDescriptor.h" @@ -26,6 +27,7 @@ #include "lldb/Core/StreamCallback.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectVariable.h" @@ -105,6 +107,8 @@ g_language_enumerators[] = FILE_AND_LINE\ "{, name = '${thread.name}'}"\ "{, queue = '${thread.queue}'}"\ + "{, activity = '${thread.info.activity.name}'}" \ + "{, ${thread.info.trace_messages} messages}" \ "{, stop reason = ${thread.stop-reason}}"\ "{\\nReturn value: ${thread.return-value}}"\ "\\n" @@ -1430,6 +1434,96 @@ IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &form return false; } +// Find information for the "thread.info.*" specifiers in a format string +static bool +FormatThreadExtendedInfoRecurse +( + const char *var_name_begin, + StructuredData::ObjectSP thread_info_dictionary, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + Stream &s +) +{ + bool var_success = false; + std::string token_format; + + llvm::StringRef var_name(var_name_begin); + size_t percent_idx = var_name.find('%'); + size_t close_curly_idx = var_name.find('}'); + llvm::StringRef path = var_name; + llvm::StringRef formatter = var_name; + + // 'path' will be the dot separated list of objects to transverse up until we hit + // a close curly brace, a percent sign, or an end of string. + if (percent_idx != llvm::StringRef::npos || close_curly_idx != llvm::StringRef::npos) + { + if (percent_idx != llvm::StringRef::npos && close_curly_idx != llvm::StringRef::npos) + { + if (percent_idx < close_curly_idx) + { + path = var_name.slice(0, percent_idx); + formatter = var_name.substr (percent_idx); + } + else + { + path = var_name.slice(0, close_curly_idx); + formatter = var_name.substr (close_curly_idx); + } + } + else if (percent_idx != llvm::StringRef::npos) + { + path = var_name.slice(0, percent_idx); + formatter = var_name.substr (percent_idx); + } + else if (close_curly_idx != llvm::StringRef::npos) + { + path = var_name.slice(0, close_curly_idx); + formatter = var_name.substr (close_curly_idx); + } + } + + StructuredData::ObjectSP value = thread_info_dictionary->GetObjectForDotSeparatedPath (path); + + if (value.get()) + { + if (value->GetType() == StructuredData::Type::eTypeInteger) + { + if (IsTokenWithFormat (formatter.str().c_str(), "", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), value->GetAsInteger()->GetValue()); + var_success = true; + } + } + else if (value->GetType() == StructuredData::Type::eTypeFloat) + { + s.Printf ("%f", value->GetAsFloat()->GetValue()); + var_success = true; + } + else if (value->GetType() == StructuredData::Type::eTypeString) + { + s.Printf("%s", value->GetAsString()->GetValue().c_str()); + var_success = true; + } + else if (value->GetType() == StructuredData::Type::eTypeArray) + { + if (value->GetAsArray()->GetSize() > 0) + { + s.Printf ("%zu", value->GetAsArray()->GetSize()); + var_success = true; + } + } + else if (value->GetType() == StructuredData::Type::eTypeDictionary) + { + s.Printf ("%zu", value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); + var_success = true; + } + } + + return var_success; +} + + static bool FormatPromptRecurse ( @@ -1969,6 +2063,15 @@ FormatPromptRecurse if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) var_success = true; } + else if (IsToken (var_name_begin, "info.")) + { + var_name_begin += ::strlen("info."); + StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); + if (object_sp && object_sp->GetType() == StructuredData::Type::eTypeDictionary) + { + var_success = FormatThreadExtendedInfoRecurse (var_name_begin, object_sp, sc, exe_ctx, s); + } + } } } } diff --git a/lldb/source/Core/StructuredData.cpp b/lldb/source/Core/StructuredData.cpp new file mode 100644 index 000000000000..8bbc8292b022 --- /dev/null +++ b/lldb/source/Core/StructuredData.cpp @@ -0,0 +1,427 @@ +//===---------------------StructuredData.cpp ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StructuredData.h" + +#include +#include +#include + +using namespace lldb_private; + + +static StructuredData::ObjectSP read_json_object (const char **ch); +static StructuredData::ObjectSP read_json_array (const char **ch); + +static StructuredData::ObjectSP +read_json_number (const char **ch) +{ + StructuredData::ObjectSP object_sp; + while (isspace (**ch)) + (*ch)++; + const char *start_of_number = *ch; + bool is_integer = true; + bool is_float = false; + while (isdigit(**ch) || **ch == '-' || **ch == '.' || **ch == '+' || **ch == 'e' || **ch == 'E') + { + if (isdigit(**ch) == false && **ch != '-') + { + is_integer = false; + is_float = true; + } + (*ch)++; + } + while (isspace (**ch)) + (*ch)++; + if (**ch == ',' || **ch == ']' || **ch == '}') + { + if (is_integer) + { + errno = 0; + uint64_t val = strtoul (start_of_number, NULL, 10); + if (errno == 0) + { + object_sp.reset(new StructuredData::Integer()); + object_sp->GetAsInteger()->SetValue (val); + } + } + if (is_float) + { + char *end_of_number = NULL; + errno = 0; + double val = strtod (start_of_number, &end_of_number); + if (errno == 0 && end_of_number != start_of_number && end_of_number != NULL) + { + object_sp.reset(new StructuredData::Float()); + object_sp->GetAsFloat()->SetValue (val); + } + } + } + return object_sp; +} + +static std::string +read_json_string (const char **ch) +{ + std::string string; + if (**ch == '"') + { + (*ch)++; + while (**ch != '\0') + { + if (**ch == '"') + { + (*ch)++; + while (isspace (**ch)) + (*ch)++; + break; + } + else if (**ch == '\\') + { + switch (**ch) + { + case '"': + string.push_back('"'); + *ch += 2; + break; + case '\\': + string.push_back('\\'); + *ch += 2; + break; + case '/': + string.push_back('/'); + *ch += 2; + break; + case 'b': + string.push_back('\b'); + *ch += 2; + break; + case 'f': + string.push_back('\f'); + *ch += 2; + break; + case 'n': + string.push_back('\n'); + *ch += 2; + break; + case 'r': + string.push_back('\r'); + *ch += 2; + break; + case 't': + string.push_back('\t'); + *ch += 2; + break; + case 'u': + // FIXME handle four-hex-digits + *ch += 10; + break; + default: + *ch += 1; + } + } + else + { + string.push_back (**ch); + } + (*ch)++; + } + } + return string; +} + +static StructuredData::ObjectSP +read_json_value (const char **ch) +{ + StructuredData::ObjectSP object_sp; + while (isspace (**ch)) + (*ch)++; + + if (**ch == '{') + { + object_sp = read_json_object (ch); + } + else if (**ch == '[') + { + object_sp = read_json_array (ch); + } + else if (**ch == '"') + { + std::string string = read_json_string (ch); + object_sp.reset(new StructuredData::String()); + object_sp->GetAsString()->SetValue(string); + } + else + { + if (strncmp (*ch, "true", 4) == 0) + { + object_sp.reset(new StructuredData::Boolean()); + object_sp->GetAsBoolean()->SetValue(true); + *ch += 4; + } + else if (strncmp (*ch, "false", 5) == 0) + { + object_sp.reset(new StructuredData::Boolean()); + object_sp->GetAsBoolean()->SetValue(false); + *ch += 5; + } + else if (strncmp (*ch, "null", 4) == 0) + { + object_sp.reset(new StructuredData::Null()); + *ch += 4; + } + else + { + object_sp = read_json_number (ch); + } + } + return object_sp; +} + +static StructuredData::ObjectSP +read_json_array (const char **ch) +{ + StructuredData::ObjectSP object_sp; + if (**ch == '[') + { + (*ch)++; + while (isspace (**ch)) + (*ch)++; + + bool first_value = true; + while (**ch != '\0' && (first_value || **ch == ',')) + { + if (**ch == ',') + (*ch)++; + first_value = false; + while (isspace (**ch)) + (*ch)++; + lldb_private::StructuredData::ObjectSP value_sp = read_json_value (ch); + if (value_sp) + { + if (object_sp.get() == NULL) + { + object_sp.reset(new StructuredData::Array()); + } + object_sp->GetAsArray()->Push (value_sp); + } + while (isspace (**ch)) + (*ch)++; + } + if (**ch == ']') + { + // FIXME should throw an error if we don't see a } to close out the JSON object + (*ch)++; + while (isspace (**ch)) + (*ch)++; + } + } + return object_sp; +} + +static StructuredData::ObjectSP +read_json_object (const char **ch) +{ + StructuredData::ObjectSP object_sp; + if (**ch == '{') + { + (*ch)++; + while (isspace (**ch)) + (*ch)++; + bool first_pair = true; + while (**ch != '\0' && (first_pair || **ch == ',')) + { + first_pair = false; + if (**ch == ',') + (*ch)++; + while (isspace (**ch)) + (*ch)++; + if (**ch != '"') + break; + std::string key_string = read_json_string (ch); + while (isspace (**ch)) + (*ch)++; + if (key_string.size() > 0 && **ch == ':') + { + (*ch)++; + while (isspace (**ch)) + (*ch)++; + lldb_private::StructuredData::ObjectSP value_sp = read_json_value (ch); + if (value_sp.get()) + { + if (object_sp.get() == NULL) + { + object_sp.reset(new StructuredData::Dictionary()); + } + object_sp->GetAsDictionary()->AddItem (key_string.c_str(), value_sp); + } + } + while (isspace (**ch)) + (*ch)++; + } + if (**ch == '}') + { + // FIXME should throw an error if we don't see a } to close out the JSON object + (*ch)++; + while (isspace (**ch)) + (*ch)++; + } + } + return object_sp; +} + + +StructuredData::ObjectSP +StructuredData::ParseJSON (std::string json_text) +{ + StructuredData::ObjectSP object_sp; + const size_t json_text_size = json_text.size(); + if (json_text_size > 0) + { + const char *start_of_json_text = json_text.c_str(); + const char *c = json_text.c_str(); + while (*c != '\0' && c - start_of_json_text <= json_text_size) + { + while (isspace (*c) && c - start_of_json_text < json_text_size) + c++; + if (*c == '{') + { + object_sp = read_json_object (&c); + } + else + { + // We have bad characters here, this is likely an illegal JSON string. + return object_sp; + } + } + } + return object_sp; +} + +StructuredData::ObjectSP +StructuredData::Object::GetObjectForDotSeparatedPath (llvm::StringRef path) +{ + if (this->GetType() == Type::eTypeDictionary) + { + std::pair match = path.split('.'); + std::string key = match.first.str(); + ObjectSP value = this->GetAsDictionary()->GetValueForKey (key.c_str()); + if (value.get()) + { + // Do we have additional words to descend? If not, return the + // value we're at right now. + if (match.second.empty()) + { + return value; + } + else + { + return value->GetObjectForDotSeparatedPath (match.second); + } + } + return ObjectSP(); + } + + if (this->GetType() == Type::eTypeArray) + { + std::pair match = path.split('['); + if (match.second.size() == 0) + { + return this->shared_from_this(); + } + errno = 0; + uint64_t val = strtoul (match.second.str().c_str(), NULL, 10); + if (errno == 0) + { + return this->GetAsArray()->GetItemAtIndex(val); + } + return ObjectSP(); + } + + return this->shared_from_this(); +} + +void +StructuredData::Array::Dump (Stream &s) const +{ + s << "["; + const size_t arrsize = m_items.size(); + for (size_t i = 0; i < arrsize; ++i) + { + m_items[i]->Dump(s); + if (i + 1 < arrsize) + s << ","; + } + s << "]"; +} + +void +StructuredData::Integer::Dump (Stream &s) const +{ + s.Printf ("%" PRIu64, m_value); +} + + +void +StructuredData::Float::Dump (Stream &s) const +{ + s.Printf ("%lf", m_value); +} + +void +StructuredData::Boolean::Dump (Stream &s) const +{ + if (m_value == true) + s.PutCString ("true"); + else + s.PutCString ("false"); +} + + +void +StructuredData::String::Dump (Stream &s) const +{ + std::string quoted; + const size_t strsize = m_value.size(); + for (size_t i = 0; i < strsize ; ++i) + { + char ch = m_value[i]; + if (ch == '"') + quoted.push_back ('\\'); + quoted.push_back (ch); + } + s.Printf ("\"%s\"", quoted.c_str()); +} + +void +StructuredData::Dictionary::Dump (Stream &s) const +{ + bool have_printed_one_elem = false; + s << "{"; + for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter) + { + if (have_printed_one_elem == false) + { + have_printed_one_elem = true; + } + else + { + s << ","; + } + s << "\"" << iter->first.AsCString() << "\":"; + iter->second->Dump(s); + } + s << "}"; +} + +void +StructuredData::Null::Dump (Stream &s) const +{ + s << "null"; +} diff --git a/lldb/source/DataFormatters/CXXFormatterFunctions.cpp b/lldb/source/DataFormatters/CXXFormatterFunctions.cpp index 04f090dd15b6..ac535ef70f0d 100644 --- a/lldb/source/DataFormatters/CXXFormatterFunctions.cpp +++ b/lldb/source/DataFormatters/CXXFormatterFunctions.cpp @@ -20,7 +20,6 @@ #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" -#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Target.h" #include @@ -965,6 +964,61 @@ ReadAsciiBufferAndDumpToStream (lldb::addr_t location, return true; } +bool +lldb_private::formatters::NSTaggedString_SummaryProvider (ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream& stream) +{ + if (!descriptor) + return false; + uint64_t len_bits = 0, data_bits = 0; + if (!descriptor->GetTaggedPointerInfo(&len_bits,&data_bits,nullptr)) + return false; + + static const int g_MaxNonBitmaskedLen = 7; //TAGGED_STRING_UNPACKED_MAXLEN + static const int g_SixbitMaxLen = 9; + static const int g_fiveBitMaxLen = 11; + + static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX"; + + if (len_bits > g_fiveBitMaxLen) + return false; + + // this is a fairly ugly trick - pretend that the numeric value is actually a char* + // this works under a few assumptions: + // little endian architecture + // sizeof(uint64_t) > g_MaxNonBitmaskedLen + if (len_bits <= g_MaxNonBitmaskedLen) + { + stream.Printf("@\"%s\"",(const char*)&data_bits); + return true; + } + + // if the data is bitmasked, we need to actually process the bytes + uint8_t bitmask = 0; + uint8_t shift_offset = 0; + + if (len_bits <= g_SixbitMaxLen) + { + bitmask = 0x03f; + shift_offset = 6; + } + else + { + bitmask = 0x01f; + shift_offset = 5; + } + + std::vector bytes; + bytes.resize(len_bits); + for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) + { + uint8_t packed = data_bits & bitmask; + bytes.insert(bytes.begin(), sixBitToCharLookup[packed]); + } + + stream.Printf("@\"%s\"",&bytes[0]); + return true; +} + bool lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream) { @@ -994,6 +1048,12 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& if (!class_name || !*class_name) return false; + bool is_tagged_ptr = (0 == strcmp(class_name,"NSTaggedPointerString")) && descriptor->GetTaggedPointerInfo(); + // for a tagged pointer, the descriptor has everything we need + if (is_tagged_ptr) + return NSTaggedString_SummaryProvider(descriptor, stream); + + // if not a tagged pointer that we know about, try the normal route uint64_t info_bits_location = valobj_addr + ptr_size; if (process_sp->GetByteOrder() != lldb::eByteOrderLittle) info_bits_location += 3; diff --git a/lldb/source/DataFormatters/NSArray.cpp b/lldb/source/DataFormatters/NSArray.cpp index 749719193c98..16635381f94f 100644 --- a/lldb/source/DataFormatters/NSArray.cpp +++ b/lldb/source/DataFormatters/NSArray.cpp @@ -19,12 +19,214 @@ #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" #include "lldb/Target/Target.h" +#include "clang/AST/ASTContext.h" + using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +namespace lldb_private { + namespace formatters { + class NSArrayMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update() = 0; + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayMSyntheticFrontEnd () {} + + protected: + virtual lldb::addr_t + GetDataAddress () = 0; + + virtual uint64_t + GetUsedCount () = 0; + + virtual uint64_t + GetOffset () = 0; + + virtual uint64_t + GetSize () = 0; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayMSyntheticFrontEnd_109 : public NSArrayMSyntheticFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used; + uint32_t _priv1 : 2 ; + uint32_t _size : 30; + uint32_t _priv2 : 2; + uint32_t _offset : 30; + uint32_t _priv3; + uint32_t _data; + }; + struct DataDescriptor_64 + { + uint64_t _used; + uint64_t _priv1 : 2 ; + uint64_t _size : 62; + uint64_t _priv2 : 2; + uint64_t _offset : 62; + uint32_t _priv3; + uint64_t _data; + }; + public: + NSArrayMSyntheticFrontEnd_109 (lldb::ValueObjectSP valobj_sp); + + virtual bool + Update(); + + virtual + ~NSArrayMSyntheticFrontEnd_109 (); + + protected: + virtual lldb::addr_t + GetDataAddress (); + + virtual uint64_t + GetUsedCount (); + + virtual uint64_t + GetOffset (); + + virtual uint64_t + GetSize (); + + private: + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + }; + + class NSArrayMSyntheticFrontEnd_1010 : public NSArrayMSyntheticFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used; + uint32_t _offset; + uint32_t _size : 28; + uint64_t _priv1 : 4; + uint32_t _priv2; + uint32_t _data; + }; + struct DataDescriptor_64 + { + uint64_t _used; + uint64_t _offset; + uint64_t _size : 60; + uint64_t _priv1 : 4; + uint32_t _priv2; + uint64_t _data; + }; + public: + NSArrayMSyntheticFrontEnd_1010 (lldb::ValueObjectSP valobj_sp); + + virtual bool + Update(); + + virtual + ~NSArrayMSyntheticFrontEnd_1010 (); + + protected: + virtual lldb::addr_t + GetDataAddress (); + + virtual uint64_t + GetUsedCount (); + + virtual uint64_t + GetOffset (); + + virtual uint64_t + GetSize (); + + private: + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + }; + + class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + uint64_t m_items; + lldb::addr_t m_data_ptr; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayCodeRunningSyntheticFrontEnd (); + }; + } +} + bool lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream) { @@ -90,42 +292,52 @@ lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& s } lldb_private::formatters::NSArrayMSyntheticFrontEnd::NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : - SyntheticChildrenFrontEnd(*valobj_sp.get()), +SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), - m_data_32(NULL), - m_data_64(NULL) +m_id_type(), +m_children() { if (valobj_sp) { - clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); + clang::ASTContext *ast = valobj_sp->GetExecutionContextRef().GetTargetSP()->GetScratchClangASTContext()->getASTContext(); if (ast) m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); + if (valobj_sp->GetProcessSP()) + m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize(); } } +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::NSArrayMSyntheticFrontEnd_109 (lldb::ValueObjectSP valobj_sp) : +NSArrayMSyntheticFrontEnd(valobj_sp), +m_data_32(NULL), +m_data_64(NULL) +{ +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::NSArrayMSyntheticFrontEnd_1010 (lldb::ValueObjectSP valobj_sp) : +NSArrayMSyntheticFrontEnd(valobj_sp), +m_data_32(NULL), +m_data_64(NULL) +{ +} + size_t lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren () { - if (m_data_32) - return m_data_32->_used; - if (m_data_64) - return m_data_64->_used; - return 0; + return GetUsedCount(); } lldb::ValueObjectSP lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx) { - if (!m_data_32 && !m_data_64) - return lldb::ValueObjectSP(); if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); - lldb::addr_t object_at_idx = (m_data_32 ? m_data_32->_data : m_data_64->_data); + lldb::addr_t object_at_idx = GetDataAddress(); size_t pyhs_idx = idx; - pyhs_idx += (m_data_32 ? m_data_32->offset : m_data_64->offset); - if ((m_data_32 ? m_data_32->_size : m_data_64->_size) <= pyhs_idx) - pyhs_idx -= (m_data_32 ? m_data_32->_size : m_data_64->_size); + pyhs_idx += GetOffset(); + if (GetSize() <= pyhs_idx) + pyhs_idx -= GetSize(); object_at_idx += (pyhs_idx * m_ptr_size); StreamString idx_name; idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); @@ -138,7 +350,42 @@ lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx } bool -lldb_private::formatters::NSArrayMSyntheticFrontEnd::Update() +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + return false; +} + +bool +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::Update() { m_children.clear(); ValueObjectSP valobj_sp = m_backend.GetSP(); @@ -181,8 +428,6 @@ lldb_private::formatters::NSArrayMSyntheticFrontEnd::MightHaveChildren () size_t lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) { - if (!m_data_32 && !m_data_64) - return UINT32_MAX; const char* item_name = name.GetCString(); uint32_t idx = ExtractIndexFromString(item_name); if (idx < UINT32_MAX && idx >= CalculateNumChildren()) @@ -190,7 +435,87 @@ lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName (co return idx; } -lldb_private::formatters::NSArrayMSyntheticFrontEnd::~NSArrayMSyntheticFrontEnd () +lldb::addr_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetDataAddress () +{ + if (!m_data_32 && !m_data_64) + return LLDB_INVALID_ADDRESS; + return m_data_32 ? m_data_32->_data : + m_data_64->_data; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetUsedCount () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_used : + m_data_64->_used; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetOffset () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_offset : + m_data_64->_offset; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetSize () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_size : + m_data_64->_size; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::~NSArrayMSyntheticFrontEnd_109 () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +lldb::addr_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetDataAddress () +{ + if (!m_data_32 && !m_data_64) + return LLDB_INVALID_ADDRESS; + return m_data_32 ? m_data_32->_data : + m_data_64->_data; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetUsedCount () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_used : + m_data_64->_used; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetOffset () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_offset : + m_data_64->_offset; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetSize () +{ + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_size : + m_data_64->_size; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::~NSArrayMSyntheticFrontEnd_1010 () { delete m_data_32; m_data_32 = NULL; @@ -286,14 +611,20 @@ lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex (size_t idx SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); if (!process_sp) return NULL; - ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + AppleObjCRuntime *runtime = (AppleObjCRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (!runtime) return NULL; - if (!valobj_sp->IsPointerType()) + ClangASTType valobj_type(valobj_sp->GetClangType()); + Flags flags(valobj_type.GetTypeInfo()); + + if (flags.IsClear(ClangASTType::eTypeIsPointer)) { Error error; valobj_sp = valobj_sp->AddressOf(error); @@ -317,7 +648,10 @@ SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCre } else if (!strcmp(class_name,"__NSArrayM")) { - return (new NSArrayMSyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() >= 1100) + return (new NSArrayMSyntheticFrontEnd_1010(valobj_sp)); + else + return (new NSArrayMSyntheticFrontEnd_109(valobj_sp)); } else { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 9b9fef98c747..182688dab66c 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -75,6 +75,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_qXfer_libraries_read (eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read (eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), + m_supports_jThreadExtendedInfo (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), m_supports_qfProcessInfo (true), m_supports_qUserName (true), @@ -495,6 +496,24 @@ GDBRemoteCommunicationClient::GetpPacketSupported (lldb::tid_t tid) return m_supports_p; } +bool +GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported () +{ + if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) + { + StringExtractorGDBRemote response; + m_supports_jThreadExtendedInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == PacketResult::Success) + { + if (response.IsOKResponse()) + { + m_supports_jThreadExtendedInfo = eLazyBoolYes; + } + } + } + return m_supports_jThreadExtendedInfo; +} + bool GDBRemoteCommunicationClient::GetxPacketSupported () { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 6b1aec53928f..1166a6f90254 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -511,6 +511,9 @@ public: bool AvoidGPackets(ProcessGDBRemote *process); + bool + GetThreadExtendedInfoSupported(); + protected: PacketResult @@ -555,6 +558,7 @@ protected: lldb_private::LazyBool m_supports_qXfer_libraries_read; lldb_private::LazyBool m_supports_qXfer_libraries_svr4_read; lldb_private::LazyBool m_supports_augmented_libraries_svr4_read; + lldb_private::LazyBool m_supports_jThreadExtendedInfo; bool m_supports_qProcessInfoPID:1, diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index b79a6974455d..7adf9165a3ef 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -56,6 +56,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/SystemRuntime.h" #include "lldb/Utility/PseudoTerminal.h" // Project includes @@ -3149,6 +3150,50 @@ ProcessGDBRemote::GetAuxvData() return buf; } +StructuredData::ObjectSP +ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) +{ + StructuredData::ObjectSP object_sp; + + if (m_gdb_comm.GetThreadExtendedInfoSupported()) + { + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + SystemRuntime *runtime = GetSystemRuntime(); + if (runtime) + { + runtime->AddThreadExtendedInfoPacketHints (args_dict); + } + args_dict->GetAsDictionary()->AddIntegerItem ("thread", tid); + + StreamString packet; + packet << "jThreadExtendedInfo:"; + args_dict->Dump (packet); + + // FIXME the final character of a JSON dictionary, '}', is the escape + // character in gdb-remote binary mode. lldb currently doesn't escape + // these characters in its packet output -- so we add the quoted version + // of the } character here manually in case we talk to a debugserver which + // un-escapes the chracters at packet read time. + packet << (char) (0x7d ^ 0x20); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) + { + StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) + { + if (!response.Empty()) + { + // The packet has already had the 0x7d xor quoting stripped out at the + // GDBRemoteCommunication packet receive level. + object_sp = StructuredData::ParseJSON (response.GetStringRef()); + } + } + } + } + return object_sp; +} + // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. // diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 1547cb67af5f..c7177e91d167 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -23,6 +23,7 @@ #include "lldb/Core/Error.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/StringList.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" @@ -307,6 +308,9 @@ protected: virtual const lldb::DataBufferSP GetAuxvData(); + lldb_private::StructuredData::ObjectSP + GetExtendedInfoForThread (lldb::tid_t tid); + void GetMaxMemorySize(); diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index 79893cf6612d..5c70cb5427cb 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -142,6 +142,23 @@ ThreadGDBRemote::GetQueueLibdispatchQueueAddress () return dispatch_queue_t_addr; } +StructuredData::ObjectSP +ThreadGDBRemote::FetchThreadExtendedInfo () +{ + StructuredData::ObjectSP object_sp; + const lldb::user_id_t tid = GetProtocolID(); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); + if (log) + log->Printf ("Fetching extended information for thread %4.4" PRIx64, tid); + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + ProcessGDBRemote *gdb_process = static_cast(process_sp.get()); + object_sp = gdb_process->GetExtendedInfoForThread (tid); + } + return object_sp; +} + void ThreadGDBRemote::WillResume (StateType resume_state) { diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h index 65d6aa5d95fb..a204a917ecb3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -12,6 +12,7 @@ #include +#include "lldb/Core/StructuredData.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" @@ -86,6 +87,9 @@ public: m_thread_dispatch_qaddr = thread_dispatch_qaddr; } + lldb_private::StructuredData::ObjectSP + FetchThreadExtendedInfo (); + protected: friend class ProcessGDBRemote; diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp index f3f1fa31cd78..c04c42b1279a 100644 --- a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp +++ b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp @@ -16,6 +16,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" #include "lldb/Expression/ClangFunction.h" #include "lldb/Expression/ClangUtilityFunction.h" #include "lldb/Host/FileSpec.h" @@ -28,7 +29,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/Process.h" - #include "SystemRuntimeMacOSX.h" using namespace lldb; @@ -93,7 +93,13 @@ SystemRuntimeMacOSX::SystemRuntimeMacOSX (Process* process) : m_page_to_free_size(0), m_lib_backtrace_recording_info(), m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), - m_libdispatch_offsets() + m_libdispatch_offsets(), + m_libpthread_layout_offsets_addr (LLDB_INVALID_ADDRESS), + m_libpthread_offsets(), + m_dispatch_tsd_indexes_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_tsd_indexes(), + m_dispatch_voucher_offsets_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_voucher_offsets() { } @@ -214,6 +220,30 @@ SystemRuntimeMacOSX::GetQueueKind (addr_t dispatch_queue_addr) return kind; } +void +SystemRuntimeMacOSX::AddThreadExtendedInfoPacketHints (lldb_private::StructuredData::ObjectSP dict_sp) +{ + StructuredData::Dictionary *dict = dict_sp->GetAsDictionary(); + if (dict) + { + ReadLibpthreadOffsets(); + if (m_libpthread_offsets.IsValid()) + { + dict->AddIntegerItem ("plo_pthread_tsd_base_offset", m_libpthread_offsets.plo_pthread_tsd_base_offset); + dict->AddIntegerItem ("plo_pthread_tsd_base_address_offset", m_libpthread_offsets.plo_pthread_tsd_base_address_offset); + dict->AddIntegerItem ("plo_pthread_tsd_entry_size", m_libpthread_offsets.plo_pthread_tsd_entry_size); + } + + ReadLibdispatchTSDIndexes (); + if (m_libdispatch_tsd_indexes.IsValid()) + { + dict->AddIntegerItem ("dti_queue_index", m_libdispatch_tsd_indexes.dti_queue_index); + dict->AddIntegerItem ("dti_voucher_index", m_libdispatch_tsd_indexes.dti_voucher_index); + dict->AddIntegerItem ("dti_qos_class_index", m_libdispatch_tsd_indexes.dti_qos_class_index); + } + } +} + bool SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread (ThreadSP thread_sp) { @@ -312,6 +342,152 @@ SystemRuntimeMacOSX::ReadLibdispatchOffsets () } } +void +SystemRuntimeMacOSX::ReadLibpthreadOffsetsAddress () +{ + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libpthread_layout_offsets_symbol_name ("pthread_layout_offsets"); + const Symbol *libpthread_layout_offsets_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libsystem_pthread.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libpthread_layout_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libpthread_layout_offsets_symbol_name, eSymbolTypeData); + if (libpthread_layout_offsets_symbol) + { + m_libpthread_layout_offsets_addr = libpthread_layout_offsets_symbol->GetAddress().GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibpthreadOffsets () +{ + if (m_libpthread_offsets.IsValid()) + return; + + ReadLibpthreadOffsetsAddress (); + + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + { + uint8_t memory_buffer[sizeof (struct LibpthreadOffsets)]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + Error error; + if (m_process->ReadMemory (m_libpthread_layout_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t data_offset = 0; + + // The struct LibpthreadOffsets is a series of uint16_t's - extract them all + // in one big go. + data.GetU16 (&data_offset, &m_libpthread_offsets.plo_version, sizeof (struct LibpthreadOffsets) / sizeof (uint16_t)); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexesAddress () +{ + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libdispatch_tsd_indexes_symbol_name ("dispatch_tsd_indexes"); + const Symbol *libdispatch_tsd_indexes_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libdispatch.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libdispatch_tsd_indexes_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libdispatch_tsd_indexes_symbol_name, eSymbolTypeData); + if (libdispatch_tsd_indexes_symbol) + { + m_dispatch_tsd_indexes_addr = libdispatch_tsd_indexes_symbol->GetAddress().GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexes () +{ + if (m_libdispatch_tsd_indexes.IsValid()) + return; + + ReadLibdispatchTSDIndexesAddress (); + + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + { + size_t maximum_tsd_indexes_struct_size; + Address dti_struct_addr; + uint16_t dti_version = 2; + if (m_process->GetTarget().ResolveLoadAddress(m_dispatch_tsd_indexes_addr, dti_struct_addr)) + { + Error error; + uint16_t version = m_process->GetTarget().ReadUnsignedIntegerFromMemory (dti_struct_addr, false, 2, UINT16_MAX, error); + if (error.Success() && dti_version != UINT16_MAX) + { + dti_version = version; + } + } + if (dti_version == 1) + { + if (m_process->GetAddressByteSize() == 4) + { + maximum_tsd_indexes_struct_size = 4 + 4 + 4 + 4; + } + else + { + maximum_tsd_indexes_struct_size = 8 + 8 + 8 + 8; + } + } + else + { + maximum_tsd_indexes_struct_size = 2 + 2 + 2 + 2; + } + + uint8_t memory_buffer[maximum_tsd_indexes_struct_size]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + Error error; + if (m_process->ReadMemory (m_dispatch_tsd_indexes_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t offset = 0; + + if (dti_version == 1) + { + m_libdispatch_tsd_indexes.dti_version = data.GetU16 (&offset); + // word alignment to next item + if (m_process->GetAddressByteSize() == 4) + { + offset += 2; + } + else + { + offset += 6; + } + m_libdispatch_tsd_indexes.dti_queue_index = data.GetPointer (&offset); + m_libdispatch_tsd_indexes.dti_voucher_index = data.GetPointer (&offset); + m_libdispatch_tsd_indexes.dti_qos_class_index = data.GetPointer (&offset); + } + else + { + m_libdispatch_tsd_indexes.dti_version = data.GetU16 (&offset); + m_libdispatch_tsd_indexes.dti_queue_index = data.GetU16 (&offset); + m_libdispatch_tsd_indexes.dti_voucher_index = data.GetU16 (&offset); + m_libdispatch_tsd_indexes.dti_qos_class_index = data.GetU16 (&offset); + } + } + } +} + ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceThread (ThreadSP real_thread, ConstString type) diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h index 30956fa3ece7..48d1f7fefb32 100644 --- a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h +++ b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h @@ -19,10 +19,11 @@ // Other libraries and framework includes #include "lldb/Target/SystemRuntime.h" -#include "lldb/Host/FileSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/ModuleList.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" #include "lldb/Host/Mutex.h" #include "lldb/Target/Process.h" #include "lldb/Target/QueueItem.h" @@ -108,6 +109,9 @@ public: virtual lldb::QueueKind GetQueueKind (lldb::addr_t dispatch_queue_addr); + virtual void + AddThreadExtendedInfoPacketHints (lldb_private::StructuredData::ObjectSP dict); + virtual bool SafeToCallFunctionsOnThisThread (lldb::ThreadSP thread_sp); @@ -176,6 +180,13 @@ private: uint16_t dqo_running; uint16_t dqo_running_size; + uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + LibdispatchOffsets () { dqo_version = UINT16_MAX; @@ -184,6 +195,10 @@ private: dqo_label = UINT16_MAX; dqo_width = UINT16_MAX; dqo_running = UINT16_MAX; + dqo_suspend_cnt = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_priority = UINT16_MAX; }; bool @@ -199,6 +214,62 @@ private: } }; + struct LibdispatchVoucherOffsets + { + uint16_t vo_version; + uint16_t vo_activity_ids_count; + uint16_t vo_activity_ids_count_size; + uint16_t vo_activity_ids_array; + uint16_t vo_activity_ids_array_entry_size; + + LibdispatchVoucherOffsets () : + vo_version (UINT16_MAX), + vo_activity_ids_count (UINT16_MAX), + vo_activity_ids_count_size (UINT16_MAX), + vo_activity_ids_array (UINT16_MAX), + vo_activity_ids_array_entry_size (UINT16_MAX) + { } + + bool IsValid () { return vo_version != UINT16_MAX; } + }; + + struct LibdispatchTSDIndexes + { + uint16_t dti_version; + uint64_t dti_queue_index; + uint64_t dti_voucher_index; + uint64_t dti_qos_class_index; + + LibdispatchTSDIndexes () : + dti_version (UINT16_MAX), + dti_queue_index (UINT64_MAX), + dti_voucher_index (UINT64_MAX), + dti_qos_class_index (UINT64_MAX) + { } + + bool IsValid () { return dti_version != UINT16_MAX; } + }; + + struct LibpthreadOffsets + { + uint16_t plo_version; + uint16_t plo_pthread_tsd_base_offset; + uint16_t plo_pthread_tsd_base_address_offset; + uint16_t plo_pthread_tsd_entry_size; + + LibpthreadOffsets () : + plo_version (UINT16_MAX), + plo_pthread_tsd_base_offset (UINT16_MAX), + plo_pthread_tsd_base_address_offset (UINT16_MAX), + plo_pthread_tsd_entry_size (UINT16_MAX) + { + } + + bool IsValid () + { + return plo_version != UINT16_MAX; + } + }; // The libBacktraceRecording function __introspection_dispatch_queue_get_pending_items has // two forms. It can either return a simple array of item_refs (void *) size or it can return @@ -225,6 +296,18 @@ private: void ReadLibdispatchOffsets (); + void + ReadLibpthreadOffsetsAddress(); + + void + ReadLibpthreadOffsets (); + + void + ReadLibdispatchTSDIndexesAddress (); + + void + ReadLibdispatchTSDIndexes (); + PendingItemsForQueue GetPendingItemRefsForQueue (lldb::addr_t queue); @@ -239,9 +322,19 @@ private: lldb::addr_t m_page_to_free; uint64_t m_page_to_free_size; libBacktraceRecording_info m_lib_backtrace_recording_info; + lldb::addr_t m_dispatch_queue_offsets_addr; struct LibdispatchOffsets m_libdispatch_offsets; + lldb::addr_t m_libpthread_layout_offsets_addr; + struct LibpthreadOffsets m_libpthread_offsets; + + lldb::addr_t m_dispatch_tsd_indexes_addr; + struct LibdispatchTSDIndexes m_libdispatch_tsd_indexes; + + lldb::addr_t m_dispatch_voucher_offsets_addr; + struct LibdispatchVoucherOffsets m_libdispatch_voucher_offsets; + DISALLOW_COPY_AND_ASSIGN (SystemRuntimeMacOSX); }; diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index af5a9a45b25b..400d9923d859 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -292,7 +292,9 @@ Thread::Thread (Process &process, lldb::tid_t tid, bool use_invalid_index_id) : m_temporary_resume_state (eStateRunning), m_unwinder_ap (), m_destroy_called (false), - m_override_should_notify (eLazyBoolCalculate) + m_override_should_notify (eLazyBoolCalculate), + m_extended_info_fetched (false), + m_extended_info () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) @@ -1709,6 +1711,9 @@ Thread::ClearStackFrames () if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched()) m_prev_frames_sp.swap (m_curr_frames_sp); m_curr_frames_sp.reset(); + + m_extended_info.reset(); + m_extended_info_fetched = false; } lldb::StackFrameSP @@ -2068,6 +2073,82 @@ Thread::GetStatus (Stream &strm, uint32_t start_frame, uint32_t num_frames, uint return num_frames_shown; } +bool +Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json) +{ + DumpUsingSettingsFormat (strm, 0); + strm.Printf("\n"); + + StructuredData::ObjectSP thread_info = GetExtendedInfo(); + + if (thread_info && print_json) + { + thread_info->Dump (strm); + strm.Printf("\n"); + return true; + } + + if (thread_info) + { + StructuredData::ObjectSP activity = thread_info->GetObjectForDotSeparatedPath("activity"); + StructuredData::ObjectSP breadcrumb = thread_info->GetObjectForDotSeparatedPath("breadcrumb"); + StructuredData::ObjectSP messages = thread_info->GetObjectForDotSeparatedPath("trace_messages"); + + bool printed_activity = false; + if (activity && activity->GetType() == StructuredData::Type::eTypeDictionary) + { + StructuredData::Dictionary *activity_dict = activity->GetAsDictionary(); + StructuredData::ObjectSP id = activity_dict->GetValueForKey("id"); + StructuredData::ObjectSP name = activity_dict->GetValueForKey("name"); + if (name && name->GetType() == StructuredData::Type::eTypeString + && id && id->GetType() == StructuredData::Type::eTypeInteger) + { + strm.Printf(" Activity '%s', 0x%" PRIx64 "\n", name->GetAsString()->GetValue().c_str(), id->GetAsInteger()->GetValue()); + } + printed_activity = true; + } + bool printed_breadcrumb = false; + if (breadcrumb && breadcrumb->GetType() == StructuredData::Type::eTypeDictionary) + { + if (printed_activity) + strm.Printf ("\n"); + StructuredData::Dictionary *breadcrumb_dict = breadcrumb->GetAsDictionary(); + StructuredData::ObjectSP breadcrumb_text = breadcrumb_dict->GetValueForKey ("name"); + if (breadcrumb_text && breadcrumb_text->GetType() == StructuredData::Type::eTypeString) + { + strm.Printf (" Current Breadcrumb: %s\n", breadcrumb_text->GetAsString()->GetValue().c_str()); + } + printed_breadcrumb = true; + } + if (messages && messages->GetType() == StructuredData::Type::eTypeArray) + { + if (printed_breadcrumb) + strm.Printf("\n"); + StructuredData::Array *messages_array = messages->GetAsArray(); + const size_t msg_count = messages_array->GetSize(); + if (msg_count > 0) + { + strm.Printf (" %zu trace messages:\n", msg_count); + for (size_t i = 0; i < msg_count; i++) + { + StructuredData::ObjectSP message = messages_array->GetItemAtIndex(i); + if (message && message->GetType() == StructuredData::Type::eTypeDictionary) + { + StructuredData::Dictionary *message_dict = message->GetAsDictionary(); + StructuredData::ObjectSP message_text = message_dict->GetValueForKey ("message"); + if (message_text && message_text->GetType() == StructuredData::Type::eTypeString) + { + strm.Printf (" %s\n", message_text->GetAsString()->GetValue().c_str()); + } + } + } + } + } + } + + return true; +} + size_t Thread::GetStackFrameStatus (Stream& strm, uint32_t first_frame, diff --git a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj index ce63f89d696b..b30eb8794c48 100644 --- a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj +++ b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -40,9 +40,10 @@ 26CE05C3115C36580022F371 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; }; 26CE05C4115C36590022F371 /* CFData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */; }; 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; }; - 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; }; + 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; 4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */ = {isa = PBXBuildFile; fileRef = 4971AE7113D10F4F00649E37 /* HasAVX.s */; }; + AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -131,9 +132,15 @@ 49F530111331519C008956F6 /* MachRegisterStatesI386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesI386.h; sourceTree = ""; }; 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesX86_64.h; sourceTree = ""; }; 9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = ""; }; + AF0934BA18E12B92005A11FD /* Genealogy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Genealogy.h; sourceTree = ""; }; + AF0934BB18E12B92005A11FD /* GenealogySPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenealogySPI.h; sourceTree = ""; }; AF5B2E72150EB6020075D7FD /* com.apple.debugserver-secure.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "com.apple.debugserver-secure.plist"; sourceTree = ""; }; + AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; sourceTree = ""; }; AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = ""; }; AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = ""; }; + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = ""; }; + ED128B7918E1F163003F6A7B /* libpmenergy.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmenergy.dylib; path = usr/lib/libpmenergy.dylib; sourceTree = SDKROOT; }; + ED128B7A18E1F163003F6A7B /* libpmsample.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmsample.dylib; path = usr/lib/libpmsample.dylib; sourceTree = SDKROOT; }; EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.plist; sourceTree = ""; }; EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = ""; }; EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = ""; }; @@ -196,6 +203,7 @@ 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */, 260FC7320E5B290400043FC9 /* debugnub-exports */, 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */, + AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */, 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */, AF5B2E72150EB6020075D7FD /* com.apple.debugserver-secure.plist */, EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */, @@ -244,6 +252,8 @@ 26ACA3330D3E94F200A2120B /* Framework */ = { isa = PBXGroup; children = ( + ED128B7918E1F163003F6A7B /* libpmenergy.dylib */, + ED128B7A18E1F163003F6A7B /* libpmsample.dylib */, 26ACA3340D3E956300A2120B /* CoreFoundation.framework */, ); name = Framework; @@ -273,6 +283,8 @@ 26C637E60C71334A0024798E /* MacOSX */ = { isa = PBXGroup; children = ( + AF0934BA18E12B92005A11FD /* Genealogy.h */, + AF0934BB18E12B92005A11FD /* GenealogySPI.h */, 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */, 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */, 2695DE2D0D3EE55B007E4CA2 /* CFData.h */, @@ -288,6 +300,7 @@ 4971AE7013D10F4F00649E37 /* HasAVX.h */, 4971AE7113D10F4F00649E37 /* HasAVX.s */, 26C637E80C71334A0024798E /* dbgnub-mig.defs */, + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */, 26C637EF0C71334A0024798E /* MachException.h */, 26C637EE0C71334A0024798E /* MachException.cpp */, 26C637F10C71334A0024798E /* MachProcess.h */, @@ -457,6 +470,7 @@ 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */, 26CE05B6115C36390022F371 /* MachTask.mm in Sources */, 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */, + AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */, 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */, 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */, 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */, @@ -579,6 +593,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = 320.99.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -595,7 +610,15 @@ INSTALL_PATH = /usr/bin; "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/; LLDB_DEBUGSERVER = 1; - OTHER_CFLAGS = "-Wparentheses"; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx10.10internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wparentheses", "-DWITH_LOCKDOWN", @@ -603,12 +626,6 @@ "-DOS_OBJECT_USE_OBJC=0", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; - OTHER_LDFLAGS = ( - "-sectcreate", - __TEXT, - __info_plist, - "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", - ); "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( "-framework", SpringBoardServices, @@ -618,6 +635,13 @@ Foundation, "-llockdown", ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; SKIP_INSTALL = YES; @@ -634,6 +658,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COPY_PHASE_STRIP = YES; @@ -651,7 +676,15 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; LLDB_DEBUGSERVER = 1; - OTHER_CFLAGS = "-Wparentheses"; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx10.10internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wparentheses", "-DWITH_LOCKDOWN", @@ -659,12 +692,7 @@ "-DOS_OBJECT_USE_OBJC=0", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; - OTHER_LDFLAGS = ( - "-sectcreate", - __TEXT, - __info_plist, - "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", - ); + OTHER_LDFLAGS = ""; "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( "-framework", SpringBoardServices, @@ -674,6 +702,13 @@ Foundation, "-llockdown", ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; @@ -690,6 +725,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COPY_PHASE_STRIP = YES; @@ -707,7 +743,15 @@ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; INSTALL_PATH = /usr/bin; LLDB_DEBUGSERVER = 1; - OTHER_CFLAGS = "-Wparentheses"; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx10.10internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wparentheses", "-DWITH_LOCKDOWN", @@ -715,12 +759,6 @@ "-DOS_OBJECT_USE_OBJC=0", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; - OTHER_LDFLAGS = ( - "-sectcreate", - __TEXT, - __info_plist, - "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", - ); "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( "-framework", SpringBoardServices, @@ -730,6 +768,13 @@ "-framework", Foundation, ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; @@ -778,6 +823,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COPY_PHASE_STRIP = YES; @@ -795,7 +841,15 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; LLDB_DEBUGSERVER = 1; - OTHER_CFLAGS = "-Wparentheses"; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx10.10internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wparentheses", "-DWITH_LOCKDOWN", @@ -803,12 +857,6 @@ "-DOS_OBJECT_USE_OBJC=0", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; - OTHER_LDFLAGS = ( - "-sectcreate", - __TEXT, - __info_plist, - "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", - ); "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( "-framework", SpringBoardServices, @@ -818,6 +866,13 @@ "-framework", Foundation, ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index 49670214ae9d..b5c2da96932b 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -38,6 +38,8 @@ #include "MacOSX/MachProcess.h" #include "MacOSX/MachTask.h" +#include "MacOSX/Genealogy.h" +#include "MacOSX/ThreadInfo.h" #include "CFString.h" #include "DNBLog.h" #include "DNBDataRef.h" @@ -977,6 +979,73 @@ DNBStateAsString(nub_state_t state) return "nub_state_t ???"; } +Genealogy::ThreadActivitySP +DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out) +{ + Genealogy::ThreadActivitySP thread_activity_sp; + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + thread_activity_sp = procSP->GetGenealogyInfoForThread (tid, timed_out); + return thread_activity_sp; +} + +Genealogy::ProcessExecutableInfoSP +DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx) +{ + Genealogy::ProcessExecutableInfoSP image_info_sp; + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + image_info_sp = procSP->GetGenealogyImageInfo (idx); + } + return image_info_sp; +} + +ThreadInfo::QoS +DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetRequestedQoS (tid, tsd, dti_qos_class_index); + } + return ThreadInfo::QoS(); +} + +nub_addr_t +DNBGetPThreadT (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetPThreadT (tid); + } + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetDispatchQueueT (tid); + } + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + } + return INVALID_NUB_ADDRESS; +} + + const char * DNBProcessGetExecutablePath (nub_process_t pid) { diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index 24f3a186be12..6b9268ca24e5 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -14,6 +14,8 @@ #ifndef __DNB_h__ #define __DNB_h__ +#include "MacOSX/Genealogy.h" +#include "MacOSX/ThreadInfo.h" #include "DNBDefs.h" #include #include @@ -129,6 +131,13 @@ nub_bool_t DNBThreadRestoreRegisterState (nub_process_t pid, nub_thread_t nub_bool_t DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t set, const char *name, DNBRegisterValue *value); nub_bool_t DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, DNBThreadStopInfo *stop_info); const char * DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid); +Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out); +Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx); +ThreadInfo::QoS DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); +nub_addr_t DNBGetPThreadT (nub_process_t pid, nub_thread_t tid); +nub_addr_t DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid); +nub_addr_t DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); +// //---------------------------------------------------------------------- // Breakpoint functions //---------------------------------------------------------------------- diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h index 70df4ba5ea69..ad87c0f7438f 100644 --- a/lldb/tools/debugserver/source/DNBDefs.h +++ b/lldb/tools/debugserver/source/DNBDefs.h @@ -356,6 +356,8 @@ enum DNBProfileDataScanType eProfileMemoryDirtyPage = (1 << 7), // Assume eProfileMemory, get Dirty Page size as well. eProfileMemoryAnonymous = (1 << 8), // Assume eProfileMemory, get Anonymous memory as well. + eProfileEnergy = (1 << 9), + eProfileAll = 0xffffffff }; diff --git a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt index 24342c84f87a..a7e4993c2a09 100644 --- a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt +++ b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt @@ -31,6 +31,7 @@ add_lldb_executable(debugserver CFBundle.cpp CFData.cpp CFString.cpp + Genealogy.cpp MachException.cpp MachProcess.mm MachTask.mm diff --git a/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp b/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp new file mode 100644 index 000000000000..1544dc26b604 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp @@ -0,0 +1,302 @@ +///===-- Activity.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include "DNBDefs.h" +#include "Genealogy.h" +#include "GenealogySPI.h" +#include "MachThreadList.h" + +//--------------------------- +/// Constructor +//--------------------------- + +Genealogy::Genealogy () : + m_os_activity_diagnostic_for_pid (nullptr), + m_os_activity_iterate_processes (nullptr), + m_os_activity_iterate_breadcrumbs (nullptr), + m_os_activity_iterate_messages (nullptr), + m_os_activity_iterate_activities (nullptr), + m_os_trace_get_type (nullptr), + m_os_trace_copy_formatted_message (nullptr), + m_os_activity_for_thread (nullptr), + m_os_activity_for_task_thread (nullptr), + m_thread_activities(), + m_process_executable_infos(), + m_diagnosticd_call_timed_out(false) +{ + m_os_activity_diagnostic_for_pid = (bool (*)(pid_t, os_activity_t, uint32_t, os_diagnostic_block_t))dlsym (RTLD_DEFAULT, "os_activity_diagnostic_for_pid"); + m_os_activity_iterate_processes = (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_processes"); + m_os_activity_iterate_breadcrumbs = (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t))) dlsym (RTLD_DEFAULT, "os_activity_iterate_breadcrumbs"); + m_os_activity_iterate_messages = (void (*)(os_trace_message_list_t, os_activity_process_t, bool (^)(os_trace_message_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_messages"); + m_os_activity_iterate_activities = (void (*)(os_activity_list_t, os_activity_process_t, bool (^)(os_activity_entry_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_activities"); + m_os_trace_get_type = (uint8_t (*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_get_type"); + m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_copy_formatted_message"); + m_os_activity_for_thread = (os_activity_t (*)(os_activity_process_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_thread"); + m_os_activity_for_task_thread = (os_activity_t (*)(task_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_task_thread"); + m_os_activity_messages_for_thread = (os_trace_message_list_t (*) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id)) dlsym (RTLD_DEFAULT, "os_activity_messages_for_thread"); +} + +Genealogy::ThreadActivitySP +Genealogy::GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out) +{ + ThreadActivitySP activity; + // + // if we've timed out trying to get the activities, don't try again at this process stop. + // (else we'll need to hit the timeout for every thread we're asked about.) + // We'll try again at the next public stop. + + if (m_thread_activities.size() == 0 && m_diagnosticd_call_timed_out == false) + { + GetActivities(pid, thread_list, task); + } + std::map::const_iterator search; + search = m_thread_activities.find(tid); + if (search != m_thread_activities.end()) + { + activity = search->second; + } + timed_out = m_diagnosticd_call_timed_out; + return activity; +} + +void +Genealogy::Clear() +{ + m_thread_activities.clear(); + m_diagnosticd_call_timed_out = false; +} + +void +Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task) +{ + if (m_os_activity_diagnostic_for_pid != nullptr + && m_os_activity_iterate_processes != nullptr + && m_os_activity_iterate_breadcrumbs != nullptr + && m_os_activity_iterate_messages != nullptr + && m_os_activity_iterate_activities != nullptr + && m_os_trace_get_type != nullptr + && m_os_trace_copy_formatted_message != nullptr + && (m_os_activity_for_thread != nullptr || m_os_activity_for_task_thread != nullptr) + ) + { + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block BreadcrumbList breadcrumbs; + __block ActivityList activities; + __block MessageList messages; + __block std::map thread_activity_mapping; + + os_activity_diagnostic_flag_t flags = OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES | OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY; + if (m_os_activity_diagnostic_for_pid (pid, 0, flags, ^(os_activity_process_list_t processes, int error) + { + if (error == 0) + { + m_os_activity_iterate_processes (processes, ^bool(os_activity_process_t process_info) + { + if (pid == process_info->pid) + { + // Collect all the Breadcrumbs + m_os_activity_iterate_breadcrumbs (process_info, ^bool(os_activity_breadcrumb_t breadcrumb) + { + Breadcrumb bc; + bc.breadcrumb_id = breadcrumb->breadcrumb_id; + bc.activity_id = breadcrumb->activity_id; + bc.timestamp = breadcrumb->timestamp; + if (breadcrumb->name) + bc.name = breadcrumb->name; + breadcrumbs.push_back (bc); + return true; + }); + + // Collect all the Activites + m_os_activity_iterate_activities (process_info->activities, process_info, ^bool(os_activity_entry_t activity) + { + Activity ac; + ac.activity_start = activity->activity_start; + ac.activity_id = activity->activity_id; + ac.parent_id = activity->parent_id; + if (activity->activity_name) + ac.activity_name = activity->activity_name; + if (activity->reason) + ac.reason = activity->reason; + activities.push_back (ac); + return true; + }); + + + // Collect all the Messages -- messages not associated with any thread + m_os_activity_iterate_messages (process_info->messages, process_info, ^bool(os_trace_message_t trace_msg) + { + Message msg; + msg.timestamp = trace_msg->timestamp; + msg.trace_id = trace_msg->trace_id; + msg.thread = trace_msg->thread; + msg.type = m_os_trace_get_type (trace_msg); + msg.activity_id = 0; + if (trace_msg->image_uuid && trace_msg->image_path) + { + ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); + uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); + process_info_sp->image_path = trace_msg->image_path; + msg.process_info_index = AddProcessExecutableInfo (process_info_sp); + } + const char *message_text = m_os_trace_copy_formatted_message (trace_msg); + if (message_text) + msg.message = message_text; + messages.push_back (msg); + return true; + }); + + // Discover which activities are said to be running on threads currently + const nub_size_t num_threads = thread_list.NumThreads(); + for (nub_size_t i = 0; i < num_threads; ++i) + { + nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i); + os_activity_t act = 0; + if (m_os_activity_for_task_thread != nullptr) + { + act = m_os_activity_for_task_thread (task, thread_id); + } + else if (m_os_activity_for_thread != nullptr) + { + act = m_os_activity_for_thread (process_info, thread_id); + } + if (act != 0) + thread_activity_mapping[thread_id] = act; + } + + // Collect all Messages -- messages associated with a thread + + // When there's no genealogy information, an early version of os_activity_messages_for_thread + // can crash in rare circumstances. Check to see if this process has any activities before + // making the call to get messages. + if (process_info->activities != nullptr && thread_activity_mapping.size() > 0) + { + std::map::const_iterator iter; + for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) + { + nub_thread_t thread_id = iter->first; + os_activity_t act = iter->second; + os_trace_message_list_t this_thread_messages = m_os_activity_messages_for_thread (process_info, act, thread_id); + m_os_activity_iterate_messages (this_thread_messages, process_info, ^bool(os_trace_message_t trace_msg) + { + Message msg; + msg.timestamp = trace_msg->timestamp; + msg.trace_id = trace_msg->trace_id; + msg.thread = trace_msg->thread; + msg.type = m_os_trace_get_type (trace_msg); + msg.activity_id = act; + if (trace_msg->image_uuid && trace_msg->image_path) + { + ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); + uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); + process_info_sp->image_path = trace_msg->image_path; + msg.process_info_index = AddProcessExecutableInfo (process_info_sp); + } + const char *message_text = m_os_trace_copy_formatted_message (trace_msg); + if (message_text) + msg.message = message_text; + messages.push_back (msg); + return true; + }); + } + } + } + return true; + }); + } + dispatch_semaphore_signal(semaphore); + }) == true) + { + // Wait for the diagnosticd xpc calls to all finish up -- or half a second to elapse. + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2); + bool success = dispatch_semaphore_wait(semaphore, timeout) == 0; + if (!success) + { + m_diagnosticd_call_timed_out = true; + return; + } + } + + // breadcrumbs, activities, and messages have all now been filled in. + + std::map::const_iterator iter; + for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) + { + nub_thread_t thread_id = iter->first; + uint64_t activity_id = iter->second; + ActivityList::const_iterator activity_search; + bool found_activity_for_this_thread = false; + for (activity_search = activities.begin(); activity_search != activities.end(); ++activity_search) + { + if (activity_search->activity_id == activity_id) + { + found_activity_for_this_thread = true; + ThreadActivitySP thread_activity_sp (new ThreadActivity()); + thread_activity_sp->current_activity = *activity_search; + + BreadcrumbList::const_iterator breadcrumb_search; + for (breadcrumb_search = breadcrumbs.begin(); breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search) + { + if (breadcrumb_search->activity_id == activity_id) + { + thread_activity_sp->breadcrumbs.push_back (*breadcrumb_search); + } + } + MessageList::const_iterator message_search; + for (message_search = messages.begin(); message_search != messages.end(); ++message_search) + { + if (message_search->thread == thread_id) + { + thread_activity_sp->messages.push_back (*message_search); + } + } + + m_thread_activities[thread_id] = thread_activity_sp; + break; + } + } + } + } +} + +uint32_t +Genealogy::AddProcessExecutableInfo (ProcessExecutableInfoSP process_exe_info) +{ + const uint32_t info_size = m_process_executable_infos.size(); + for (uint32_t idx = 0; idx < info_size; ++idx) + { + if (uuid_compare (m_process_executable_infos[idx]->image_uuid, process_exe_info->image_uuid) == 0) + { + return idx + 1; + } + } + m_process_executable_infos.push_back (process_exe_info); + return info_size + 1; +} + +Genealogy::ProcessExecutableInfoSP +Genealogy::GetProcessExecutableInfosAtIndex(uint32_t idx) +{ + ProcessExecutableInfoSP info_sp; + if (idx > 0) + { + idx--; + if (idx <= m_process_executable_infos.size()) + { + info_sp = m_process_executable_infos[idx]; + } + } + return info_sp; +} + diff --git a/lldb/tools/debugserver/source/MacOSX/Genealogy.h b/lldb/tools/debugserver/source/MacOSX/Genealogy.h new file mode 100644 index 000000000000..18521ee23464 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/Genealogy.h @@ -0,0 +1,116 @@ +//===-- Activity.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __Genealogy_h__ +#define __Genealogy_h__ + +#include +#include +#include +#include +#include + +#include "GenealogySPI.h" +#include "MachThreadList.h" + +class Genealogy +{ +public: + + Genealogy (); + + ~Genealogy () + { + } + + void + Clear(); + + struct Breadcrumb + { + uint32_t breadcrumb_id; + uint64_t activity_id; + uint64_t timestamp; + std::string name; + }; + + struct Activity + { + uint64_t activity_start; + uint64_t activity_id; + uint64_t parent_id; + std::string activity_name; + std::string reason; + }; + + struct Message + { + uint64_t timestamp; + uint64_t activity_id; + uint64_t trace_id; + uint64_t thread; + uint8_t type; // OS_TRACE_TYPE_RELEASE, OS_TRACE_TYPE_DEBUG, OS_TRACE_TYPE_ERROR, OS_TRACE_TYPE_FAULT + uint32_t process_info_index; // index # of the image uuid/file path, 0 means unknown + std::string message; + }; + + typedef std::vector MessageList; + typedef std::vector BreadcrumbList; + typedef std::vector ActivityList; + + struct ThreadActivity + { + Activity current_activity; + MessageList messages; + BreadcrumbList breadcrumbs; // should be 0 or 1 breadcrumbs; no more than 1 BC for any given activity + }; + + typedef std::shared_ptr ThreadActivitySP; + + ThreadActivitySP + GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out); + + struct ProcessExecutableInfo + { + std::string image_path; + uuid_t image_uuid; + }; + + typedef std::shared_ptr ProcessExecutableInfoSP; + + ProcessExecutableInfoSP + GetProcessExecutableInfosAtIndex(uint32_t idx); + + uint32_t + AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info); + +private: + + void + GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task); + + // the spi we need to call into libtrace - look them up via dlsym at runtime + bool (*m_os_activity_diagnostic_for_pid) (pid_t pid, os_activity_t activity, uint32_t flags, os_diagnostic_block_t block); + void (*m_os_activity_iterate_processes) (os_activity_process_list_t processes, bool (^iterator)(os_activity_process_t process_info)); + void (*m_os_activity_iterate_breadcrumbs) (os_activity_process_t process_info, bool (^iterator)(os_activity_breadcrumb_t breadcrumb)); + void (*m_os_activity_iterate_messages) (os_trace_message_list_t messages, os_activity_process_t process_info, bool (^iterator)(os_trace_message_t tracemsg)); + void (*m_os_activity_iterate_activities) (os_activity_list_t activities, os_activity_process_t process_info, bool (^iterator)(os_activity_entry_t activity)); + uint8_t (*m_os_trace_get_type) (os_trace_message_t trace_msg); + char * (*m_os_trace_copy_formatted_message) (os_trace_message_t trace_msg); + os_activity_t (*m_os_activity_for_thread) (os_activity_process_t process, uint64_t thread_id); + os_activity_t (*m_os_activity_for_task_thread) (task_t target, uint64_t thread_id); + os_trace_message_list_t (*m_os_activity_messages_for_thread) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id); + + + std::map m_thread_activities; + std::vector m_process_executable_infos; + bool m_diagnosticd_call_timed_out; +}; + +#endif // __Genealogy_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h b/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h new file mode 100644 index 000000000000..f84e930e8725 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h @@ -0,0 +1,96 @@ +//===-- ActivitySPI.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +#ifndef __GenealogySPI_h__ +#define __GenealogySPI_h__ + +#include + +typedef void *os_activity_process_list_t; +typedef void *os_activity_list_t; +typedef void *os_trace_message_list_t; +typedef struct os_activity_watch_s *os_activity_watch_t; +typedef uint64_t os_activity_t; + +struct os_activity_breadcrumb_s { + uint32_t breadcrumb_id; + uint64_t activity_id; + uint64_t timestamp; + const char *name; +}; + +typedef struct os_activity_breadcrumb_s *os_activity_breadcrumb_t; + +typedef struct os_trace_message_s { + uint64_t trace_id; + uint64_t thread; + uint64_t timestamp; + uint32_t offset; + xpc_object_t __unsafe_unretained payload; + const uint8_t *image_uuid; + const char *image_path; + const char *format; + const void *buffer; + size_t bufferLen; +} *os_trace_message_t; + +typedef struct os_activity_process_s { + os_activity_process_list_t child_procs; + os_trace_message_list_t messages; + os_activity_list_t activities; + void *breadcrumbs; + uint64_t proc_id; + const uint8_t *image_uuid; + const char *image_path; + pid_t pid; +} *os_activity_process_t; + +typedef struct os_activity_entry_s { + uint64_t activity_start; + os_activity_t activity_id; + os_activity_t parent_id; + const char *activity_name; + const char *reason; + os_trace_message_list_t messages; +} *os_activity_entry_t; + +enum +{ + OS_ACTIVITY_DIAGNOSTIC_DEFAULT = 0x00000000, + OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY = 0x00000001, + OS_ACTIVITY_DIAGNOSTIC_SKIP_DECODE = 0x00000002, + OS_ACTIVITY_DIAGNOSTIC_FLATTENED = 0x00000004, + OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES = 0x00000008, + OS_ACTIVITY_DIAGNOSTIC_MAX = 0x0000000f +}; +typedef uint32_t os_activity_diagnostic_flag_t; + +enum +{ + OS_ACTIVITY_WATCH_DEFAULT = 0x00000000, + OS_ACTIVITY_WATCH_PROCESS_ONLY = 0x00000001, + OS_ACTIVITY_WATCH_SKIP_DECODE = 0x00000002, + OS_ACTIVITY_WATCH_PAYLOAD = 0x00000004, + OS_ACTIVITY_WATCH_ERRORS = 0x00000008, + OS_ACTIVITY_WATCH_FAULTS = 0x00000010, + OS_ACTIVITY_WATCH_MAX = 0x0000001f +}; +typedef uint32_t os_activity_watch_flag_t; + +// Return values from os_trace_get_type() +#define OS_TRACE_TYPE_RELEASE (1u << 0) +#define OS_TRACE_TYPE_DEBUG (1u << 1) +#define OS_TRACE_TYPE_ERROR ((1u << 6) | (1u << 0)) +#define OS_TRACE_TYPE_FAULT ((1u << 7) | (1u << 6) | (1u << 0)) + + +typedef void (^os_activity_watch_block_t)(os_activity_watch_t watch, os_activity_process_t process_info, bool canceled); +typedef void (^os_diagnostic_block_t)(os_activity_process_list_t processes, int error); + +#endif + diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h index 78be93c69766..9a643603f576 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -25,6 +25,8 @@ #include "PThreadCondition.h" #include "PThreadEvent.h" #include "PThreadMutex.h" +#include "Genealogy.h" +#include "ThreadInfo.h" #include #include @@ -179,6 +181,11 @@ public: nub_bool_t SyncThreadState (nub_thread_t tid); const char * ThreadGetName (nub_thread_t tid); nub_state_t ThreadGetState (nub_thread_t tid); + ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT (nub_thread_t tid); + nub_addr_t GetDispatchQueueT (nub_thread_t tid); + nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + nub_size_t GetNumThreads () const; nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const; nub_thread_t GetCurrentThread (); @@ -265,6 +272,10 @@ public: bool ProcessUsingSpringBoard() const { return (m_flags & eMachProcessFlagsUsingSBS) != 0; } bool ProcessUsingBackBoard() const { return (m_flags & eMachProcessFlagsUsingBKS) != 0; } + Genealogy::ThreadActivitySP GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out); + + Genealogy::ProcessExecutableInfoSP GetGenealogyImageInfo (size_t idx); + DNBProfileDataScanType GetProfileScanType () { return m_profile_scan_type; } private: @@ -311,6 +322,7 @@ private: PThreadMutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages MachThreadList m_thread_list; // A list of threads that is maintained/updated after each stop + Genealogy m_activities; // A list of activities that is updated after every stop lazily nub_state_t m_state; // The state of our process PThreadMutex m_state_mutex; // Multithreaded protection for m_state PThreadEvent m_events; // Process related events in the child processes lifetime can be waited upon diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm index 0ac985b1cb6d..85123977f0b2 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -126,6 +126,7 @@ MachProcess::MachProcess() : m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data (), m_thread_list (), + m_activities (), m_exception_messages (), m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), m_state (eStateUnloaded), @@ -219,6 +220,30 @@ MachProcess::SyncThreadState (nub_thread_t tid) } +ThreadInfo::QoS +MachProcess::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + return m_thread_list.GetRequestedQoS (tid, tsd, dti_qos_class_index); +} + +nub_addr_t +MachProcess::GetPThreadT (nub_thread_t tid) +{ + return m_thread_list.GetPThreadT (tid); +} + +nub_addr_t +MachProcess::GetDispatchQueueT (nub_thread_t tid) +{ + return m_thread_list.GetDispatchQueueT (tid); +} + +nub_addr_t +MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); +} + nub_thread_t MachProcess::GetCurrentThread () { @@ -365,6 +390,7 @@ MachProcess::Clear(bool detaching) PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); m_exception_messages.clear(); } + m_activities.Clear(); if (m_profile_thread) { pthread_join(m_profile_thread, NULL); @@ -605,6 +631,7 @@ MachProcess::Detach() { m_thread_actions.Clear(); + m_activities.Clear(); DNBThreadResumeAction thread_action; thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); thread_action.state = eStateRunning; @@ -1250,6 +1277,7 @@ MachProcess::ExceptionMessageBundleComplete() DNBArchProtocol::SetArchitecture (process_cpu_type); } m_thread_list.Clear(); + m_activities.Clear(); m_breakpoints.DisableAll(); } @@ -1288,6 +1316,7 @@ MachProcess::ExceptionMessageBundleComplete() // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list.ProcessDidStop(this); + m_activities.Clear(); // Let each thread know of any exceptions for (i=0; i + +#ifdef LLDB_ENERGY +#include +#include +#include +#endif + + //---------------------------------------------------------------------- // MachTask constructor //---------------------------------------------------------------------- @@ -330,6 +339,8 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) if (task == TASK_NULL) return result; + pid_t pid = m_process->ProcessID(); + struct task_basic_info task_info; DNBError err; err = BasicInfo(task, &task_info); @@ -363,7 +374,7 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) if (scanType & eProfileThreadsCPU) { - get_threads_profile_data(scanType, task, m_process->ProcessID(), threads_id, threads_name, threads_used_usec); + get_threads_profile_data(scanType, task, pid, threads_id, threads_name, threads_used_usec); } struct vm_statistics vm_stats; @@ -375,7 +386,7 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) mach_vm_size_t dirty_size = 0; mach_vm_size_t purgeable = 0; mach_vm_size_t anonymous = 0; - if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous)) + if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), pid, vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous)) { std::ostringstream profile_data_stream; @@ -436,17 +447,23 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) pagesize = PageSize(); } + /* Unused values. Optimized out for transfer performance. profile_data_stream << "wired:" << vm_stats.wire_count * pagesize << ';'; profile_data_stream << "active:" << vm_stats.active_count * pagesize << ';'; profile_data_stream << "inactive:" << vm_stats.inactive_count * pagesize << ';'; + */ uint64_t total_used_count = vm_stats.wire_count + vm_stats.inactive_count + vm_stats.active_count; profile_data_stream << "used:" << total_used_count * pagesize << ';'; + /* Unused values. Optimized out for transfer performance. profile_data_stream << "free:" << vm_stats.free_count * pagesize << ';'; + */ profile_data_stream << "rprvt:" << rprvt << ';'; + /* Unused values. Optimized out for transfer performance. profile_data_stream << "rsize:" << rsize << ';'; profile_data_stream << "vprvt:" << vprvt << ';'; profile_data_stream << "vsize:" << vsize << ';'; + */ if (scanType & eProfileMemoryDirtyPage) profile_data_stream << "dirty:" << dirty_size << ';'; @@ -458,6 +475,39 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) } } + // proc_pid_rusage pm_sample_task_and_pid pm_energy_impact needs to be tested for weakness in Cab +#ifdef LLDB_ENERGY + if ((scanType & eProfileEnergy) && (pm_sample_task_and_pid != NULL)) + { + struct rusage_info_v2 info; + int rc = proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t *)&info); + if (rc == 0) + { + uint64_t now = mach_absolute_time(); + pm_task_energy_data_t pm_energy; + memset(&pm_energy, 0, sizeof(pm_energy)); + /* + * Disable most features of pm_sample_pid. It will gather + * network/GPU/WindowServer information; fill in the rest. + */ + pm_sample_task_and_pid(task, pid, &pm_energy, now, PM_SAMPLE_ALL & ~PM_SAMPLE_NAME & ~PM_SAMPLE_INTERVAL & ~PM_SAMPLE_CPU & ~PM_SAMPLE_DISK); + pm_energy.sti.total_user = info.ri_user_time; + pm_energy.sti.total_system = info.ri_system_time; + pm_energy.sti.task_interrupt_wakeups = info.ri_interrupt_wkups; + pm_energy.sti.task_platform_idle_wakeups = info.ri_pkg_idle_wkups; + pm_energy.diskio_bytesread = info.ri_diskio_bytesread; + pm_energy.diskio_byteswritten = info.ri_diskio_byteswritten; + pm_energy.pageins = info.ri_pageins; + + uint64_t total_energy = (uint64_t)(pm_energy_impact(&pm_energy) * NSEC_PER_SEC); + //uint64_t process_age = now - info.ri_proc_start_abstime; + //uint64_t avg_energy = 100.0 * (double)total_energy / (double)process_age; + + profile_data_stream << "energy:" << total_energy << ';'; + } + } +#endif + profile_data_stream << "--end--;"; result = profile_data_stream.str(); diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.cpp b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp index f3a9d51c8085..f0650d34783a 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThread.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp @@ -12,10 +12,13 @@ //===----------------------------------------------------------------------===// #include +#include +#include #include "MachThread.h" #include "MachProcess.h" #include "DNBLog.h" #include "DNB.h" +#include "ThreadInfo.h" static uint32_t GetSequenceID() @@ -24,7 +27,7 @@ GetSequenceID() return ++g_nextID; } -MachThread::MachThread (MachProcess *process, uint64_t unique_thread_id, thread_t mach_port_num) : +MachThread::MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id, thread_t mach_port_num) : m_process (process), m_unique_id (unique_thread_id), m_mach_port_number (mach_port_num), @@ -38,12 +41,16 @@ MachThread::MachThread (MachProcess *process, uint64_t unique_thread_id, thread_ m_num_reg_sets (0), m_ident_info(), m_proc_threadinfo(), - m_dispatch_queue_name() + m_dispatch_queue_name(), + m_is_64_bit(is_64_bit), + m_pthread_qos_class_decode (nullptr) { nub_size_t num_reg_sets = 0; m_reg_sets = m_arch_ap->GetRegisterSetInfo (&num_reg_sets); m_num_reg_sets = num_reg_sets; + m_pthread_qos_class_decode = (unsigned int (*)(unsigned long, int*, unsigned long*)) dlsym (RTLD_DEFAULT, "_pthread_qos_class_decode"); + // Get the thread state so we know if a thread is in a state where we can't // muck with it and also so we get the suspend count correct in case it was // already suspended @@ -719,3 +726,197 @@ MachThread::GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id) } return tident.thread_id; } + +nub_addr_t +MachThread::GetPThreadT () +{ + nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS; + if (MachPortNumberIsValid (m_mach_port_number)) + { + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr == KERN_SUCCESS) + { + // Dereference thread_handle to get the pthread_t value for this thread. + if (m_is_64_bit) + { + uint64_t addr; + if (m_process->ReadMemory (tident.thread_handle, 8, &addr) == 8) + { + if (addr != 0) + { + pthread_t_value = addr; + } + } + } + else + { + uint32_t addr; + if (m_process->ReadMemory (tident.thread_handle, 4, &addr) == 4) + { + if (addr != 0) + { + pthread_t_value = addr; + } + } + } + } + } + return pthread_t_value; +} + +// Return this thread's TSD (Thread Specific Data) address. +// This is computed based on this thread's pthread_t value. +// +// We compute the TSD from the pthread_t by one of two methods. +// +// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we add to +// the pthread_t to get the TSD base address. +// +// Else we read a pointer from memory at pthread_t + plo_pthread_tsd_base_address_offset and +// that gives us the TSD address. +// +// These plo_pthread_tsd_base values must be read out of libpthread by lldb & provided to debugserver. + +nub_addr_t +MachThread::GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + nub_addr_t tsd_addr = INVALID_NUB_ADDRESS; + nub_addr_t pthread_t_value = GetPThreadT(); + if (plo_pthread_tsd_base_offset != 0 && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) + { + tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset; + } + else + { + if (plo_pthread_tsd_entry_size == 4) + { + uint32_t addr = 0; + if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 4, &addr) == 4) + { + if (addr != 0) + { + tsd_addr = addr; + } + } + } + if (plo_pthread_tsd_entry_size == 4) + { + uint64_t addr = 0; + if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 8, &addr) == 8) + { + if (addr != 0) + { + tsd_addr = addr; + } + } + } + } + return tsd_addr; +} + + +nub_addr_t +MachThread::GetDispatchQueueT () +{ + nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS; + if (MachPortNumberIsValid (m_mach_port_number)) + { + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 && tident.dispatch_qaddr != INVALID_NUB_ADDRESS) + { + // Dereference dispatch_qaddr to get the dispatch_queue_t value for this thread's queue, if any. + if (m_is_64_bit) + { + uint64_t addr; + if (m_process->ReadMemory (tident.dispatch_qaddr, 8, &addr) == 8) + { + if (addr != 0) + dispatch_queue_t_value = addr; + } + } + else + { + uint32_t addr; + if (m_process->ReadMemory (tident.dispatch_qaddr, 4, &addr) == 4) + { + if (addr != 0) + dispatch_queue_t_value = addr; + } + } + } + } + return dispatch_queue_t_value; +} + + +ThreadInfo::QoS +MachThread::GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + ThreadInfo::QoS qos_value; + if (MachPortNumberIsValid (m_mach_port_number) && m_pthread_qos_class_decode != nullptr) + { + uint64_t pthread_priority_value = 0; + if (m_is_64_bit) + { + uint64_t pri; + if (m_process->ReadMemory (tsd + (dti_qos_class_index * 8), 8, &pri) == 8) + { + pthread_priority_value = pri; + } + } + else + { + uint32_t pri; + if (m_process->ReadMemory (tsd + (dti_qos_class_index * 4), 4, &pri) == 4) + { + pthread_priority_value = pri; + } + } + + uint32_t requested_qos = m_pthread_qos_class_decode (pthread_priority_value, NULL, NULL); + + switch (requested_qos) + { + // These constants from + case 0x21: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE"; + qos_value.printable_name = "User Interactive"; + break; + case 0x19: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_USER_INITIATED"; + qos_value.printable_name = "User Initiated"; + break; + case 0x15: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_DEFAULT"; + qos_value.printable_name = "Default"; + break; + case 0x11: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_UTILITY"; + qos_value.printable_name = "Utility"; + break; + case 0x09: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_BACKGROUND"; + qos_value.printable_name = "Background"; + break; + case 0x00: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_UNSPECIFIED"; + qos_value.printable_name = "Unspecified"; + break; + } + } + return qos_value; +} diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.h b/lldb/tools/debugserver/source/MacOSX/MachThread.h index c3121112980c..a4667603c44c 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThread.h +++ b/lldb/tools/debugserver/source/MacOSX/MachThread.h @@ -28,6 +28,8 @@ #include "DNBArch.h" #include "DNBRegisterInfo.h" +#include "ThreadInfo.h" + class DNBBreakpoint; class MachProcess; class MachThreadList; @@ -36,7 +38,7 @@ class MachThread { public: - MachThread (MachProcess *process, uint64_t unique_thread_id = 0, thread_t mach_port_number = 0); + MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id = 0, thread_t mach_port_number = 0); ~MachThread (); MachProcess * Process() { return m_process; } @@ -111,6 +113,11 @@ public: return m_arch_ap.get(); } + ThreadInfo::QoS GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT(); + nub_addr_t GetDispatchQueueT(); + nub_addr_t GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + static uint64_t GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id); protected: @@ -138,6 +145,10 @@ protected: thread_identifier_info_data_t m_ident_info; struct proc_threadinfo m_proc_threadinfo; std::string m_dispatch_queue_name; + bool m_is_64_bit; + + // qos_class_t _pthread_qos_class_decode(pthread_priority_t priority, int *, unsigned long *); + unsigned int (*m_pthread_qos_class_decode) (unsigned long priority, int*, unsigned long *); private: friend class MachThreadList; diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp index 634d2576332d..68b272a6bd76 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -22,7 +22,8 @@ MachThreadList::MachThreadList() : m_threads(), - m_threads_mutex(PTHREAD_MUTEX_RECURSIVE) + m_threads_mutex(PTHREAD_MUTEX_RECURSIVE), + m_is_64_bit(false) { } @@ -48,6 +49,42 @@ MachThreadList::GetName (nub_thread_t tid) return NULL; } +ThreadInfo::QoS +MachThreadList::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index); + return ThreadInfo::QoS(); +} + +nub_addr_t +MachThreadList::GetPThreadT (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetPThreadT(); + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +MachThreadList::GetDispatchQueueT (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetDispatchQueueT(); + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +MachThreadList::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + return INVALID_NUB_ADDRESS; +} + nub_thread_t MachThreadList::SetCurrentThread(nub_thread_t tid) { @@ -276,19 +313,18 @@ MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadLi int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID() }; struct kinfo_proc processInfo; size_t bufsize = sizeof(processInfo); - bool is_64_bit = false; if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) { if (processInfo.kp_proc.p_flag & P_LP64) - is_64_bit = true; + m_is_64_bit = true; } #if defined (__i386__) || defined (__x86_64__) - if (is_64_bit) + if (m_is_64_bit) DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); else DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); #elif defined (__arm__) || defined (__arm64__) - if (is_64_bit) + if (m_is_64_bit) DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); else DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); @@ -326,7 +362,7 @@ MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadLi else { // We don't have this thread, lets add it. - thread_sp.reset(new MachThread(process, unique_thread_id, mach_port_num)); + thread_sp.reset(new MachThread(process, m_is_64_bit, unique_thread_id, mach_port_num)); // Add the new thread regardless of its is user ready state... // Make sure the thread is ready to be displayed and shown to users diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.h b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h index 46bccfee8dee..5fac2c4b457e 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThreadList.h +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h @@ -15,6 +15,7 @@ #define __MachThreadList_h__ #include "MachThread.h" +#include "ThreadInfo.h" class DNBThreadResumeActions; @@ -40,6 +41,12 @@ public: const char * GetName (nub_thread_t tid); nub_state_t GetState (nub_thread_t tid); nub_thread_t SetCurrentThread (nub_thread_t tid); + + ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT (nub_thread_t tid); + nub_addr_t GetDispatchQueueT (nub_thread_t tid); + nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + bool GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const; void DumpThreadStoppedReason (nub_thread_t tid) const; bool GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info); @@ -73,6 +80,7 @@ protected: collection m_threads; mutable PThreadMutex m_threads_mutex; MachThreadSP m_current_thread; + bool m_is_64_bit; }; #endif // #ifndef __MachThreadList_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp index 857955b566df..df30ad637a4b 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -243,66 +243,9 @@ MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t kern_return_t kr; info_count = TASK_VM_INFO_COUNT; -#ifdef TASK_VM_INFO_PURGEABLE kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count); -#else - kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count); -#endif if (kr == KERN_SUCCESS) dirty_size = vm_info.internal; - -#else - mach_vm_address_t address = 0; - mach_vm_size_t size; - kern_return_t err = 0; - unsigned nestingDepth = 0; - mach_vm_size_t pages_resident = 0; - mach_vm_size_t pages_dirtied = 0; - - while (1) - { - mach_msg_type_number_t count; - struct vm_region_submap_info_64 info; - - count = VM_REGION_SUBMAP_INFO_COUNT_64; - err = mach_vm_region_recurse(task, &address, &size, &nestingDepth, (vm_region_info_t)&info, &count); - if (err == KERN_INVALID_ADDRESS) - { - // It seems like this is a good break too. - break; - } - else if (err) - { - mach_error("vm_region",err); - break; // reached last region - } - - bool should_count = true; - if (info.is_submap) - { // is it a submap? - nestingDepth++; - should_count = false; - } - else - { - // Don't count malloc stack logging data in the TOTAL VM usage lines. - if (info.user_tag == VM_MEMORY_ANALYSIS_TOOL) - should_count = false; - - address = address+size; - } - - if (should_count) - { - pages_resident += info.pages_resident; - pages_dirtied += info.pages_dirtied; - } - } - - vm_size_t pagesize = PageSize (task); - rsize = pages_resident * pagesize; - dirty_size = pages_dirtied * pagesize; - #endif } @@ -443,78 +386,21 @@ MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, rprvt += aliased; } -#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 -#ifndef TASK_VM_INFO_PURGEABLE -// cribbed from sysmond -static uint64_t -SumVMPurgeableInfo(const vm_purgeable_info_t info) -{ - uint64_t sum = 0; - int i; - - for (i = 0; i < 8; i++) - { - sum += info->fifo_data[i].size; - } - sum += info->obsolete_data.size; - for (i = 0; i < 8; i++) - { - sum += info->lifo_data[i].size; - } - - return sum; -} -#endif /* !TASK_VM_INFO_PURGEABLE */ -#endif - static void GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous) { #if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 kern_return_t kr; -#ifndef TASK_VM_INFO_PURGEABLE - task_purgable_info_t purgeable_info; - uint64_t purgeable_sum = 0; -#endif /* !TASK_VM_INFO_PURGEABLE */ mach_msg_type_number_t info_count; task_vm_info_data_t vm_info; -#ifndef TASK_VM_INFO_PURGEABLE - typedef kern_return_t (*task_purgable_info_type) (task_t, task_purgable_info_t *); - task_purgable_info_type task_purgable_info_ptr = NULL; - task_purgable_info_ptr = (task_purgable_info_type)dlsym(RTLD_NEXT, "task_purgable_info"); - if (task_purgable_info_ptr != NULL) - { - kr = (*task_purgable_info_ptr)(task, &purgeable_info); - if (kr == KERN_SUCCESS) { - purgeable_sum = SumVMPurgeableInfo(&purgeable_info); - purgeable = purgeable_sum; - } - } -#endif /* !TASK_VM_INFO_PURGEABLE */ - info_count = TASK_VM_INFO_COUNT; -#ifdef TASK_VM_INFO_PURGEABLE kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count); -#else - kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count); -#endif if (kr == KERN_SUCCESS) { -#ifdef TASK_VM_INFO_PURGEABLE purgeable = vm_info.purgeable_volatile_resident; anonymous = vm_info.internal - vm_info.purgeable_volatile_pmap; -#else - if (purgeable_sum < vm_info.internal) - { - anonymous = vm_info.internal - purgeable_sum; - } - else - { - anonymous = 0; - } -#endif } #endif diff --git a/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h b/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h new file mode 100644 index 000000000000..1fd9d5790cf0 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h @@ -0,0 +1,26 @@ +//===-- ThreadInfo.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __ThreadInfo_h__ +#define __ThreadInfo_h__ + +namespace ThreadInfo { + +class QoS { +public: + QoS () : constant_name(), printable_name(), enum_value(UINT32_MAX) { } + bool IsValid () { return enum_value != UINT32_MAX; } + std::string constant_name; + std::string printable_name; + uint32_t enum_value; +}; + +}; + +#endif // __ThreadInfo_h__ diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index ccc0a9ceaa56..e8fb114d2ba2 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -28,9 +28,11 @@ #include "RNBServices.h" #include "RNBSocket.h" #include "Utility/StringExtractor.h" +#include "MacOSX/Genealogy.h" #include #include +#include #include // for endianness predefines //---------------------------------------------------------------------- @@ -175,6 +177,7 @@ RNBRemote::CreatePacketTable () t.push_back (Packet (query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion, NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); // t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups")); + t.push_back (Packet (json_query_thread_extended_info, &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specifc packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command")); t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_QSetLogging , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix")); @@ -850,6 +853,57 @@ decode_binary_data (const char *str, size_t len) return bytes; } +// Quote any meta characters in a std::string as per the binary +// packet convention in the gdb-remote protocol. + +std::string +binary_encode_string (const std::string &s) +{ + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '#' || ch == '$' || ch == '}' || ch == '*') + { + output.push_back ('}'); // 0x7d + output.push_back (ch ^ 0x20); + } + else + { + output.push_back (ch); + } + } + return output; +} + +// If the value side of a key-value pair in JSON is a string, +// and that string has a " character in it, the " character must +// be escaped. + +std::string +json_string_quote_metachars (const std::string &s) +{ + if (s.find('"') == std::string::npos) + return s; + + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '"') + { + output.push_back ('\\'); + } + output.push_back (ch); + } + return output; +} + typedef struct register_map_entry { uint32_t gdb_regnum; // gdb register number @@ -2135,6 +2189,18 @@ append_hex_value (std::ostream& ostrm, const uint8_t* buf, size_t buf_size, bool } } +void +append_hexified_string (std::ostream& ostrm, const std::string &string) +{ + size_t string_size = string.size(); + const char *string_buf = string.c_str(); + for (size_t i = 0; i < string_size; i++) + { + ostrm << RAWHEX8(*(string_buf + i)); + } +} + + void register_value_in_hex_fixed_width (std::ostream& ostrm, @@ -3963,6 +4029,274 @@ RNBRemote::HandlePacket_qGDBServerVersion (const char *p) return SendPacket (strm.str()); } +// A helper function that retrieves a single integer value from +// a one-level-deep JSON dictionary of key-value pairs. e.g. +// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] +// +uint64_t +get_integer_value_for_key_name_from_json (const char *key, const char *json_string) +{ + uint64_t retval = INVALID_NUB_ADDRESS; + std::string key_with_quotes = "\""; + key_with_quotes += key; + key_with_quotes += "\":"; + const char *c = strstr (json_string, key_with_quotes.c_str()); + if (c) + { + c += key_with_quotes.size(); + errno = 0; + retval = strtoul (c, NULL, 10); + if (errno != 0) + { + retval = INVALID_NUB_ADDRESS; + } + } + return retval; + +} + +rnb_err_t +RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p) +{ + nub_process_t pid; + std::ostringstream json; + std::ostringstream reply_strm; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E81"); + } + + pid = m_ctx.ProcessID(); + + const char thread_extended_info_str[] = { "jThreadExtendedInfo:{" }; + if (strncmp (p, thread_extended_info_str, sizeof (thread_extended_info_str) - 1) == 0) + { + p += strlen (thread_extended_info_str); + + uint64_t tid = get_integer_value_for_key_name_from_json ("thread", p); + uint64_t plo_pthread_tsd_base_address_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_address_offset", p); + uint64_t plo_pthread_tsd_base_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_offset", p); + uint64_t plo_pthread_tsd_entry_size = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_entry_size", p); + uint64_t dti_qos_class_index = get_integer_value_for_key_name_from_json ("dti_qos_class_index", p); + // Commented out the two variables below as they are not being used +// uint64_t dti_queue_index = get_integer_value_for_key_name_from_json ("dti_queue_index", p); +// uint64_t dti_voucher_index = get_integer_value_for_key_name_from_json ("dti_voucher_index", p); + + if (tid != INVALID_NUB_ADDRESS) + { + nub_addr_t pthread_t_value = DNBGetPThreadT (pid, tid); + + uint64_t tsd_address = INVALID_NUB_ADDRESS; + if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS + && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS + && plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) + { + tsd_address = DNBGetTSDAddressForThread (pid, tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + } + + bool timed_out = false; + Genealogy::ThreadActivitySP thread_activity_sp; + + // If the pthread_t value is invalid, or if we were able to fetch the thread's TSD base + // and got an invalid value back, then we have a thread in early startup or shutdown and + // it's possible that gathering the genealogy information for this thread go badly. + // Ideally fetching this info for a thread in these odd states shouldn't matter - but + // we've seen some problems with these new SPI and threads in edge-casey states. + + double genealogy_fetch_time = 0; + if (pthread_t_value != INVALID_NUB_ADDRESS && tsd_address != INVALID_NUB_ADDRESS) + { + DNBTimer timer(false); + thread_activity_sp = DNBGetGenealogyInfoForThread (pid, tid, timed_out); + genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0; + } + + std::unordered_set process_info_indexes; // an array of the process info #'s seen + + json << "{"; + + bool need_to_print_comma = false; + + if (thread_activity_sp && timed_out == false) + { + const Genealogy::Activity *activity = &thread_activity_sp->current_activity; + bool need_vouchers_comma_sep = false; + json << "\"activity_query_timed_out\":false,"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + if (activity->activity_id != 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + need_vouchers_comma_sep = true; + json << "\"activity\":{"; + json << "\"start\":" << activity->activity_start << ","; + json << "\"id\":" << activity->activity_id << ","; + json << "\"parent_id\":" << activity->parent_id << ","; + json << "\"name\":\"" << json_string_quote_metachars (activity->activity_name) << "\","; + json << "\"reason\":\"" << json_string_quote_metachars (activity->reason) << "\""; + json << "}"; + } + if (thread_activity_sp->messages.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"trace_messages\":["; + bool printed_one_message = false; + for (auto iter = thread_activity_sp->messages.begin() ; iter != thread_activity_sp->messages.end(); ++iter) + { + if (printed_one_message) + json << ","; + else + printed_one_message = true; + json << "{"; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"trace_id\":" << iter->trace_id << ","; + json << "\"thread\":" << iter->thread << ","; + json << "\"type\":" << (int) iter->type << ","; + json << "\"process_info_index\":" << iter->process_info_index << ","; + process_info_indexes.insert (iter->process_info_index); + json << "\"message\":\"" << json_string_quote_metachars (iter->message) << "\""; + json << "}"; + } + json << "]"; + } + if (thread_activity_sp->breadcrumbs.size() == 1) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"breadcrumb\":{"; + for (auto iter = thread_activity_sp->breadcrumbs.begin() ; iter != thread_activity_sp->breadcrumbs.end(); ++iter) + { + json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"name\":\"" << json_string_quote_metachars (iter->name) << "\""; + } + json << "}"; + } + if (process_info_indexes.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"process_infos\":["; + bool printed_one_process_info = false; + for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter) + { + if (printed_one_process_info) + json << ","; + else + printed_one_process_info = true; + Genealogy::ProcessExecutableInfoSP image_info_sp; + uint32_t idx = *iter; + image_info_sp = DNBGetGenealogyImageInfo (pid, idx); + json << "{"; + char uuid_buf[37]; + uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf); + json << "\"process_info_index\":" << idx << ","; + json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\","; + json << "\"image_uuid\":\"" << uuid_buf <<"\""; + json << "}"; + } + json << "]"; + } + } + else + { + if (timed_out) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_timed_out\":true"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + json << ","; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + } + } + + if (tsd_address != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"tsd_address\":" << tsd_address; + + if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) + { + ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread (pid, tid, tsd_address, dti_qos_class_index); + if (requested_qos.IsValid()) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"requested_qos\":{"; + json << "\"enum_value\":" << requested_qos.enum_value << ","; + json << "\"constant_name\":\"" << json_string_quote_metachars (requested_qos.constant_name) << "\","; + json << "\"printable_name\":\"" << json_string_quote_metachars (requested_qos.printable_name) << "\""; + json << "}"; + } + } + } + + if (pthread_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"pthread_t\":" << pthread_t_value; + } + + nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT (pid, tid); + if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"dispatch_queue_t\":" << dispatch_queue_t_value; + } + + json << "}"; + std::string json_quoted = binary_encode_string (json.str()); + reply_strm << json_quoted; + return SendPacket (reply_strm.str()); + } + } + return SendPacket ("OK"); +} + // Note that all numeric values returned by qProcessInfo are hex encoded, // including the pid and the cpu type. diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index 8d5f9a16d3a5..fb2ec1278c3e 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -99,6 +99,7 @@ public: query_host_info, // 'qHostInfo' query_gdb_server_version, // 'qGDBServerVersion' query_process_info, // 'qProcessInfo' + json_query_thread_extended_info,// 'jThreadExtendedInfo' pass_signals_to_inferior, // 'QPassSignals' start_noack_mode, // 'QStartNoAckMode' prefix_reg_packets_with_tid, // 'QPrefixRegisterPacketsWithThreadID @@ -181,6 +182,7 @@ public: rnb_err_t HandlePacket_qVAttachOrWaitSupported (const char *p); rnb_err_t HandlePacket_qSyncThreadStateSupported (const char *p); rnb_err_t HandlePacket_qThreadInfo (const char *p); + rnb_err_t HandlePacket_jThreadExtendedInfo (const char *p); rnb_err_t HandlePacket_qThreadExtraInfo (const char *p); rnb_err_t HandlePacket_qThreadStopInfo (const char *p); rnb_err_t HandlePacket_qHostInfo (const char *p); diff --git a/lldb/tools/debugserver/source/debugserver-entitlements.plist b/lldb/tools/debugserver/source/debugserver-entitlements.plist index 7de628db2e36..84e0ca41dfc1 100644 --- a/lldb/tools/debugserver/source/debugserver-entitlements.plist +++ b/lldb/tools/debugserver/source/debugserver-entitlements.plist @@ -14,5 +14,11 @@ debugserver + com.apple.diagnosticd.diagnostic + + com.apple.security.network.server + + com.apple.security.network.client + diff --git a/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist b/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist new file mode 100644 index 000000000000..eddbaa0063ef --- /dev/null +++ b/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.diagnosticd.diagnostic + + +