mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-26 19:36:28 +00:00
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
This commit is contained in:
parent
b4ad29be92
commit
705b180964
@ -87,6 +87,9 @@ public:
|
||||
void
|
||||
SetTrapExceptions (bool trap_exceptions = true);
|
||||
|
||||
void
|
||||
SetLanguage (lldb::LanguageType language);
|
||||
|
||||
void
|
||||
SetCancelCallback (lldb::ExpressionCancelCallback callback, void *baton);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
486
lldb/include/lldb/Core/StructuredData.h
Normal file
486
lldb/include/lldb/Core/StructuredData.h
Normal file
@ -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 <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#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<Object> ObjectSP;
|
||||
typedef std::shared_ptr<Array> ArraySP;
|
||||
typedef std::shared_ptr<Dictionary> DictionarySP;
|
||||
|
||||
enum class Type {
|
||||
eTypeInvalid = -1,
|
||||
eTypeNull = 0,
|
||||
eTypeArray,
|
||||
eTypeInteger,
|
||||
eTypeFloat,
|
||||
eTypeBoolean,
|
||||
eTypeString,
|
||||
eTypeDictionary
|
||||
};
|
||||
|
||||
class Object :
|
||||
public std::enable_shared_from_this<Object>
|
||||
{
|
||||
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<ObjectSP> 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<ConstString, ObjectSP> 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_
|
@ -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<false> (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<lldb::ValueObjectSP> 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<lldb::ValueObjectSP> 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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
//------------------------------------------------------------------
|
||||
|
@ -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 = "<group>"; };
|
||||
AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemRuntimeMacOSX.cpp; sourceTree = "<group>"; };
|
||||
AF9B8F32182DB52900DA866F /* SystemRuntimeMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemRuntimeMacOSX.h; sourceTree = "<group>"; };
|
||||
AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StructuredData.cpp; path = source/Core/StructuredData.cpp; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
@ -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 */,
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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<void*>(exe_ctx.GetThreadPtr()));
|
||||
}
|
||||
}
|
||||
|
||||
if (log)
|
||||
log->Printf ("SBThread(%p)::GetInfoItemByPathAsString () => %s",
|
||||
static_cast<void*>(exe_ctx.GetThreadPtr()),
|
||||
strm.GetData());
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
SBError
|
||||
SBThread::ResumeNewPlan (ExecutionContext &exe_ctx, ThreadPlan *new_plan)
|
||||
{
|
||||
|
@ -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<ThreadSP> 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",
|
||||
|
@ -52,6 +52,7 @@ add_lldb_library(lldbCore
|
||||
StreamGDBRemote.cpp
|
||||
StreamString.cpp
|
||||
StringList.cpp
|
||||
StructuredData.cpp
|
||||
Timer.cpp
|
||||
UserID.cpp
|
||||
UserSettingsController.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
427
lldb/source/Core/StructuredData.cpp
Normal file
427
lldb/source/Core/StructuredData.cpp
Normal file
@ -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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
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<llvm::StringRef, llvm::StringRef> 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<llvm::StringRef, llvm::StringRef> 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";
|
||||
}
|
@ -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 <algorithm>
|
||||
@ -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<uint8_t> 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;
|
||||
|
@ -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<lldb::ValueObjectSP> 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<lldb::ValueObjectSP> 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
|
||||
{
|
||||
|
@ -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 ()
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<ProcessGDBRemote *>(process_sp.get());
|
||||
object_sp = gdb_process->GetExtendedInfoForThread (tid);
|
||||
}
|
||||
return object_sp;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadGDBRemote::WillResume (StateType resume_state)
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 = "<group>"; };
|
||||
49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesX86_64.h; sourceTree = "<group>"; };
|
||||
9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = "<group>"; };
|
||||
AF0934BA18E12B92005A11FD /* Genealogy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Genealogy.h; sourceTree = "<group>"; };
|
||||
AF0934BB18E12B92005A11FD /* GenealogySPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenealogySPI.h; sourceTree = "<group>"; };
|
||||
AF5B2E72150EB6020075D7FD /* com.apple.debugserver-secure.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "com.apple.debugserver-secure.plist"; sourceTree = "<group>"; };
|
||||
AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; sourceTree = "<group>"; };
|
||||
AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = "<group>"; };
|
||||
AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = "<group>"; };
|
||||
AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = "<group>"; };
|
||||
EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = "<group>"; };
|
||||
@ -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*]" = "";
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -14,6 +14,8 @@
|
||||
#ifndef __DNB_h__
|
||||
#define __DNB_h__
|
||||
|
||||
#include "MacOSX/Genealogy.h"
|
||||
#include "MacOSX/ThreadInfo.h"
|
||||
#include "DNBDefs.h"
|
||||
#include <mach/thread_info.h>
|
||||
#include <string>
|
||||
@ -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
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,7 @@ add_lldb_executable(debugserver
|
||||
CFBundle.cpp
|
||||
CFData.cpp
|
||||
CFString.cpp
|
||||
Genealogy.cpp
|
||||
MachException.cpp
|
||||
MachProcess.mm
|
||||
MachTask.mm
|
||||
|
302
lldb/tools/debugserver/source/MacOSX/Genealogy.cpp
Normal file
302
lldb/tools/debugserver/source/MacOSX/Genealogy.cpp
Normal file
@ -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 <Availability.h>
|
||||
#include <string>
|
||||
#include <dlfcn.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#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<nub_thread_t, ThreadActivitySP>::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<nub_thread_t, uint64_t> 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<nub_thread_t, uint64_t>::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<nub_thread_t, uint64_t>::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;
|
||||
}
|
||||
|
116
lldb/tools/debugserver/source/MacOSX/Genealogy.h
Normal file
116
lldb/tools/debugserver/source/MacOSX/Genealogy.h
Normal file
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <pthread.h>
|
||||
#include <mach/task.h>
|
||||
|
||||
#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<Message> MessageList;
|
||||
typedef std::vector<Breadcrumb> BreadcrumbList;
|
||||
typedef std::vector<Activity> 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<ThreadActivity> 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<ProcessExecutableInfo> 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<nub_thread_t, ThreadActivitySP> m_thread_activities;
|
||||
std::vector<ProcessExecutableInfoSP> m_process_executable_infos;
|
||||
bool m_diagnosticd_call_timed_out;
|
||||
};
|
||||
|
||||
#endif // __Genealogy_h__
|
96
lldb/tools/debugserver/source/MacOSX/GenealogySPI.h
Normal file
96
lldb/tools/debugserver/source/MacOSX/GenealogySPI.h
Normal file
@ -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 <xpc/xpc.h>
|
||||
|
||||
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
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "PThreadCondition.h"
|
||||
#include "PThreadEvent.h"
|
||||
#include "PThreadMutex.h"
|
||||
#include "Genealogy.h"
|
||||
#include "ThreadInfo.h"
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <sys/signal.h>
|
||||
@ -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
|
||||
|
@ -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<m_exception_messages.size(); ++i)
|
||||
@ -1666,6 +1695,18 @@ MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
|
||||
return INVALID_NUB_PROCESS;
|
||||
}
|
||||
|
||||
Genealogy::ThreadActivitySP
|
||||
MachProcess::GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out)
|
||||
{
|
||||
return m_activities.GetGenealogyInfoForThread (m_pid, tid, m_thread_list, m_task.TaskPort(), timed_out);
|
||||
}
|
||||
|
||||
Genealogy::ProcessExecutableInfoSP
|
||||
MachProcess::GetGenealogyImageInfo (size_t idx)
|
||||
{
|
||||
return m_activities.GetProcessExecutableInfosAtIndex (idx);
|
||||
}
|
||||
|
||||
// Do the process specific setup for attach. If this returns NULL, then there's no
|
||||
// platform specific stuff to be done to wait for the attach. If you get non-null,
|
||||
// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
|
||||
|
@ -54,6 +54,15 @@ extern "C"
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
#ifdef LLDB_ENERGY
|
||||
#include <mach/mach_time.h>
|
||||
#include <pmenergy.h>
|
||||
#include <pmsample.h>
|
||||
#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();
|
||||
|
@ -12,10 +12,13 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <mach/thread_policy.h>
|
||||
#include <dlfcn.h>
|
||||
#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 <pthread/qos.h>
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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
|
||||
|
26
lldb/tools/debugserver/source/MacOSX/ThreadInfo.h
Normal file
26
lldb/tools/debugserver/source/MacOSX/ThreadInfo.h
Normal file
@ -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__
|
@ -28,9 +28,11 @@
|
||||
#include "RNBServices.h"
|
||||
#include "RNBSocket.h"
|
||||
#include "Utility/StringExtractor.h"
|
||||
#include "MacOSX/Genealogy.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include <TargetConditionals.h> // 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<uint32_t> 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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -14,5 +14,11 @@
|
||||
<array>
|
||||
<string>debugserver</string>
|
||||
</array>
|
||||
<key>com.apple.diagnosticd.diagnostic</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.diagnosticd.diagnostic</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Loading…
x
Reference in New Issue
Block a user