Add StructuredData plugin type; showcase with new DarwinLog feature

See docs/structured_data/StructuredDataPlugins.md for details.

differential review: https://reviews.llvm.org/D22976

reviewers: clayborg, jingham
llvm-svn: 279198
This commit is contained in:
Todd Fiala 2016-08-19 02:52:07 +00:00
parent e8529c28f1
commit aef7de8492
130 changed files with 11356 additions and 61 deletions

11
lldb/.gitignore vendored
View File

@ -40,7 +40,18 @@ __pycache__/
clang-module-cache
# Skip ctags-style tags files
tags
# We should ignore Xcode-style embedding of llvm/ at lldb root dir.
# Do not add trailing '/'s, they skip symlinks.
/llvm
/DerivedData
# Ignore test trace directories.
20??-??-??-??_??_??/
# Ignore crashlog support files.
crashinfo.lock
crashinfo.so

View File

@ -47,6 +47,7 @@ set( LLDB_USED_LIBS
lldbPluginObjectContainerMachOArchive
lldbPluginObjectContainerBSDArchive
lldbPluginPlatformMacOSX
lldbPluginStructuredDataDarwinLog
lldbPluginDynamicLoaderMacOSXDYLD
lldbPluginUnwindAssemblyInstEmulation
lldbPluginUnwindAssemblyX86

View File

@ -0,0 +1,160 @@
# Change Notes
## Summary
This document describes the DarwinLog logging feature.
## StructuredDataDarwinLog feature
The DarwinLog feature supports logging os_log*() and NSLog() messages
to the command-line lldb console, as well as making those messages
available to LLDB clients via the event system. Starting with fall
2016 OSes, Apple platforms introduce a new fire-hose, stream-style
logging system where the bulk of the log processing happens on the log
consumer side. This reduces logging impact on the system when there
are no consumers, making it cheaper to include logging at all times.
However, it also increases the work needed on the consumer end when
log messages are desired.
The debugserver binary has been modified to support collection of
os_log*()/NSLog() messages, selection of which messages appear in the
stream, and fine-grained filtering of what gets passed on to the LLDB
client. DarwinLog also tracks the activity chain (i.e. os_activity()
hierarchy) in effect at the time the log messages were issued. The
user is able to configure a number of aspects related to the
formatting of the log message header fields.
The DarwinLog support is written in a way which should support the
lldb client side on non-Apple clients talking to an Apple device or
macOS system; hence, the plugin support is built into all LLDB
clients, not just those built on an Apple platform.
StructuredDataDarwinLog implements the 'DarwinLog' feature type, and
the plugin name for it shows up as 'darwin-log'.
The user interface to the darwin-log support is via the following:
* 'plugin structured-data darwin-log enable' command
This is the main entry point for enabling the command. It can be
set before launching a process or while the process is running.
If the user wants to squelch seeing info-level or debug-level
messages, which is the default behavior, then the enable command
must be made prior to launching the process; otherwise, the
info-level and debug-level messages will always show up. Also,
there is a similar "echo os_log()/NSLog() messages to target
process stderr" mechanism which is properly disabled when enabling
the DarwinLog support prior to launch. This cannot be squelched
if enabling DarwinLog after launch.
See the help for this command. There are a number of options
to shrink or expand the number of messages that are processed
on the remote side and sent over to the client, and other
options to control the formatting of messages displayed.
This command is sticky. Once enabled, it will stay enabled for
future process launches.
* 'plugin structured-data darwin-log disable' command
Executing this command disables os_log() capture in the currently
running process and signals LLDB to stop attempting to launch
new processes with DarwinLog support enabled.
* 'settings set \
plugin.structured-data.darwin-log.enable-on-startup'
and
'settings set \
plugin.structured-data.darwin-log.auto-enable-options -- {options}'
When enable-on-startup is set to true, then LLDB will automatically
enable DarwinLog on startup of relevant processes. It will use the
content provided in the auto-enable-options settings as the
options to pass to the enable command.
Note the '--' required after auto-enable-command. That is necessary
for raw commands like settings set. The '--' will not become part
of the options for the enable command.
### Message flow and related performance considerations
os_log()-style collection is not free. The more data that must be
processed, the slower it will be. There are several knobs available
to the developer to limit how much data goes through the pipe, and how
much data ultimately goes over the wire to the LLDB client. The
user's goal should be to ensure he or she only collects as many log
messages are needed, but no more.
The flow of data looks like the following:
1. Data comes into debugserver from the low-level OS facility that
receives log messages. The data that comes through this pipe can
be limited or expanded by the '--debug', '--info' and
'--all-processes' options of the 'plugin structured-data darwin-log
enable' command. options. Exclude as many categories as possible
here (also the default). The knobs here are very coarse - for
example, whether to include os_log_info()-level or
os_log_debug()-level info, or to include callstacks in the log
message event data.
2. The debugserver process filters the messages that arrive through a
message log filter that may be fully customized by the user. It
works similar to a rules-based packet filter: a set of rules are
matched against the log message, each rule tried in sequential
order. The first rule that matches then either accepts or rejects
the message. If the log message does not match any rule, then the
message gets the no-match (i.e. fall-through) action. The no-match
action defaults to accepting but may be set to reject.
Filters can be added via the enable command's '--filter
{filter-spec}' option. Filters are added in order, and multiple
--filter entries can be provided to the enable command.
Filters take the following form:
{action} {attribute} {op}
{action} :=
accept |
reject
{attribute} :=
category | // The log message category
subsystem | // The log message subsystem}
activity | // The child-most activity in force
// at the time the message was logged.
activity-chain | // The complete activity chain, specified
// as {parent-activity}:{child-activity}:
// {grandchild-activity}
message | // The fully expanded message contents.
// Note this one is expensive because it
// requires expanding the message. Avoid
// this if possible, or add it further
// down the filter chain.
{op} :=
match {exact-match-text} |
regex {search-regex} // uses C++ std::regex
// ECMAScript variant.
e.g.
--filter "accept subsystem match com.example.mycompany.myproduct"
--filter "accept subsystem regex com.example.+"
--filter "reject category regex spammy-system-[[:digit:]]+"
3. Messages that are accepted by the log message filter get sent to
the lldb client, where they are mapped to the
StructuredDataDarwinLog plugin. By default, command-line lldb will
issue a Process-level event containing the log message content, and
will request the plugin to print the message if the plugin is
enabled to do so.
### Log message display
Several settings control aspects of displaying log messages in
command-line LLDB. See the enable command's help for a description
of these.

View File

@ -0,0 +1,136 @@
# Change Notes
## Overview
This document describes an infrastructural feature called Structured
Data plugins. See the DarwinLog.md doc for a description of one
such plugin that makes use of this feature.
## StructuredDataPlugin
StructuredDataPlugin instances have the following characteristics:
* Each plugin instance is bound to a single Process instance.
* Each StructuredData feature has a type name that identifies the
feature. For instance, the type name for the DarwinLog feature is
"DarwinLog". This feature type name is used in various places.
* The process monitor reports the list of supported StructuredData
features advertised by the process monitor. Process goes through the
list of supported feature type names, and asks each known
StructuredDataPlugin if it can handle the feature. The first plugin
that supports the feature is mapped to that Process instance for
that feature. Plugins are only mapped when the process monitor
advertises that a feature is supported.
* The feature may send asynchronous messages in StructuredData format
to the Process instance. Process instances route the asynchronous
structured data messages to the plugin mapped to that feature type,
if one exists.
* Plugins can request that the Process instance forward on
configuration data to the process monitor if the plugin needs/wants
to configure the feature. Plugins may call the new Process method
```C++
virtual Error
ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp)
```
where type_name is the feature name and config_sp points to the
configuration structured data, which may be nullptr.
* Plugins for features present in a process are notified when modules
are loaded into the Process instance via this StructuredDataPlugin
method:
```C++
virtual void
ModulesDidLoad(Process &process, ModuleList &module_list);
```
* Plugins may optionally broadcast their received structured data as
an LLDB process-level event via the following new Process call:
```C++
void
BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
const lldb::StructuredDataPluginSP &plugin_sp);
```
IDE clients might use this feature to receive information about the
process as it is running to monitor memory usage, CPU usage, and
logging.
Internally, the event type created is an instance of
EventDataStructuredData.
* In the case where a plugin chooses to broadcast a received
StructuredData event, the command-line LLDB Debugger instance
listens for them. The Debugger instance then gives the plugin an
opportunity to display info to either the debugger output or error
stream at a time that is safe to write to them. The plugin can
choose to display something appropriate regarding the structured
data that time.
* Plugins can provide a ProcessLaunchInfo filter method when the
plugin is registered. If such a filter method is provided, then
when a process is about to be launched for debugging, the filter
callback is invoked, given both the launch info and the target. The
plugin may then alter the launch info if needed to better support
the feature of the plugin.
* The plugin is entirely independent of the type of Process-derived
class that it is working with. The only requirements from the
process monitor are the following feature-agnostic elements:
* Provide a way to discover features supported by the process
monitor for the current process.
* Specify the list of supported feature type names to Process.
The process monitor does this by calling the following new
method on Process:
```C++
void
MapSupportedStructuredDataPlugins(const StructuredData::Array
&supported_type_names)
```
The supported_type_names specifies an array of string entries,
where each entry specifies the name of a StructuredData feature.
* Provide a way to forward on configuration data for a feature type
to the process monitor. This is the manner by which LLDB can
configure a feature, perhaps based on settings or commands from
the user. The following virtual method on Process (described
earlier) does the job:
```C++
virtual Error
ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp)
```
* Listen for asynchronous structured data packets from the process
monitor, and forward them on to Process via this new Process
member method:
```C++
bool
RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp)
```
* StructuredData producers must send their top-level data as a
Dictionary type, with a key called 'type' specifying a string value,
where the value is equal to the StructuredData feature/type name
previously advertised. Everything else about the content of the
dictionary is entirely up to the feature.
* StructuredDataPlugin commands show up under 'plugin structured-data
plugin-name'.
* StructuredDataPlugin settings show up under
'plugin.structured-data.{plugin-name}.

View File

@ -55,6 +55,7 @@
#include "lldb/API/SBSourceManager.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStringList.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBSymbol.h"
#include "lldb/API/SBSymbolContext.h"
#include "lldb/API/SBSymbolContextList.h"

View File

@ -71,6 +71,7 @@ class LLDB_API SBSection;
class LLDB_API SBSourceManager;
class LLDB_API SBStream;
class LLDB_API SBStringList;
class LLDB_API SBStructuredData;
class LLDB_API SBSymbol;
class LLDB_API SBSymbolContext;
class LLDB_API SBSymbolContextList;

View File

@ -73,6 +73,7 @@ protected:
friend class SBHostOS;
friend class SBPlatform;
friend class SBProcess;
friend class SBStructuredData;
friend class SBThread;
friend class SBTarget;
friend class SBValue;

View File

@ -32,7 +32,8 @@ public:
eBroadcastBitInterrupt = (1 << 1),
eBroadcastBitSTDOUT = (1 << 2),
eBroadcastBitSTDERR = (1 << 3),
eBroadcastBitProfileData = (1 << 4)
eBroadcastBitProfileData = (1 << 4),
eBroadcastBitStructuredData = (1 << 5)
};
SBProcess ();
@ -278,10 +279,16 @@ public:
static bool
GetInterruptedFromEvent (const lldb::SBEvent &event);
static lldb::SBStructuredData
GetStructuredDataFromEvent (const lldb::SBEvent &event);
static bool
EventIsProcessEvent (const lldb::SBEvent &event);
static bool
EventIsStructuredDataEvent (const lldb::SBEvent &event);
lldb::SBBroadcaster
GetBroadcaster () const;

View File

@ -83,6 +83,7 @@ protected:
friend class SBProcess;
friend class SBSection;
friend class SBSourceManager;
friend class SBStructuredData;
friend class SBSymbol;
friend class SBSymbolContext;
friend class SBSymbolContextList;

View File

@ -0,0 +1,52 @@
//===-- SBStructuredData.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef SBStructuredData_h
#define SBStructuredData_h
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBModule.h"
namespace lldb {
class SBStructuredData
{
public:
SBStructuredData();
SBStructuredData(const lldb::SBStructuredData &rhs);
SBStructuredData(const lldb::EventSP &event_sp);
~SBStructuredData();
lldb::SBStructuredData &
operator =(const lldb::SBStructuredData &rhs);
bool
IsValid() const;
void
Clear();
lldb::SBError
GetAsJSON(lldb::SBStream &stream) const;
lldb::SBError
GetDescription(lldb::SBStream &stream) const;
private:
class Impl;
std::unique_ptr<Impl> m_impl_up;
};
}
#endif /* SBStructuredData_h */

View File

@ -272,11 +272,10 @@ private:
/// //----------------------------------------------------------
/// enum
/// {
/// eBroadcastBitStateChanged = (1 << 0),
/// eBroadcastBitInterrupt = (1 << 1),
/// eBroadcastBitSTDOUT = (1 << 2),
/// eBroadcastBitSTDERR = (1 << 3),
/// eBroadcastBitProfileData = (1 << 4)
/// eBroadcastBitOne = (1 << 0),
/// eBroadcastBitTwo = (1 << 1),
/// eBroadcastBitThree = (1 << 2),
/// ...
/// };
/// \endcode
//----------------------------------------------------------------------

View File

@ -20,6 +20,7 @@
// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/StructuredData.h"
#include "lldb/Host/Predicate.h"
#include "lldb/Core/Broadcaster.h"
@ -158,6 +159,80 @@ private:
}
};
//----------------------------------------------------------------------
/// This class handles one or more StructuredData::Dictionary entries
/// that are raised for structured data events.
//----------------------------------------------------------------------
class EventDataStructuredData : public EventData
{
public:
//------------------------------------------------------------------
// Constructors
//------------------------------------------------------------------
EventDataStructuredData();
EventDataStructuredData(const lldb::ProcessSP &process_sp,
const StructuredData::ObjectSP &object_sp,
const lldb::StructuredDataPluginSP &plugin_sp);
~EventDataStructuredData() override;
//------------------------------------------------------------------
// Member functions
//------------------------------------------------------------------
const ConstString &
GetFlavor() const override;
void
Dump(Stream *s) const override;
const lldb::ProcessSP&
GetProcess() const;
const StructuredData::ObjectSP&
GetObject() const;
const lldb::StructuredDataPluginSP&
GetStructuredDataPlugin() const;
void
SetProcess(const lldb::ProcessSP &process_sp);
void
SetObject(const StructuredData::ObjectSP &object_sp);
void
SetStructuredDataPlugin(const lldb::StructuredDataPluginSP &plugin_sp);
//------------------------------------------------------------------
// Static functions
//------------------------------------------------------------------
static const EventDataStructuredData*
GetEventDataFromEvent(const Event *event_ptr);
static lldb::ProcessSP
GetProcessFromEvent(const Event *event_ptr);
static StructuredData::ObjectSP
GetObjectFromEvent(const Event *event_ptr);
static lldb::StructuredDataPluginSP
GetPluginFromEvent(const Event *event_ptr);
static const ConstString &
GetFlavorString ();
private:
lldb::ProcessSP m_process_sp;
StructuredData::ObjectSP m_object_sp;
lldb::StructuredDataPluginSP m_plugin_sp;
DISALLOW_COPY_AND_ASSIGN(EventDataStructuredData);
};
//----------------------------------------------------------------------
// lldb::Event
//----------------------------------------------------------------------

View File

@ -327,6 +327,65 @@ public:
GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
CommandInterpreter &interpreter);
//------------------------------------------------------------------
// StructuredDataPlugin
//------------------------------------------------------------------
//------------------------------------------------------------------
/// Register a StructuredDataPlugin class along with optional
/// callbacks for debugger initialization and Process launch info
/// filtering and manipulation.
///
/// @param[in] name
/// The name of the plugin.
///
/// @param[in] description
/// A description string for the plugin.
///
/// @param[in] create_callback
/// The callback that will be invoked to create an instance of
/// the callback. This may not be nullptr.
///
/// @param[in] debugger_init_callback
/// An optional callback that will be made when a Debugger
/// instance is initialized.
///
/// @param[in] filter_callback
/// An optional callback that will be invoked before LLDB
/// launches a process for debugging. The callback must
/// do the following:
/// 1. Only do something if the plugin's behavior is enabled.
/// 2. Only make changes for processes that are relevant to the
/// plugin. The callback gets a pointer to the Target, which
/// can be inspected as needed. The ProcessLaunchInfo is
/// provided in read-write mode, and may be modified by the
/// plugin if, for instance, additional environment variables
/// are needed to support the feature when enabled.
///
/// @return
/// Returns true upon success; otherwise, false.
//------------------------------------------------------------------
static bool
RegisterPlugin(const ConstString &name,
const char *description,
StructuredDataPluginCreateInstance create_callback,
DebuggerInitializeCallback debugger_init_callback = nullptr,
StructuredDataFilterLaunchInfo filter_callback
= nullptr);
static bool
UnregisterPlugin(StructuredDataPluginCreateInstance create_callback);
static StructuredDataPluginCreateInstance
GetStructuredDataPluginCreateCallbackAtIndex(uint32_t idx);
static StructuredDataPluginCreateInstance
GetStructuredDataPluginCreateCallbackForPluginName(const ConstString &name);
static StructuredDataFilterLaunchInfo
GetStructuredDataFilterCallbackAtIndex(uint32_t idx,
bool &iteration_complete);
//------------------------------------------------------------------
// SymbolFile
//------------------------------------------------------------------
@ -531,6 +590,16 @@ public:
static bool CreateSettingForOperatingSystemPlugin(Debugger &debugger,
const lldb::OptionValuePropertiesSP &properties_sp,
const ConstString &description, bool is_global_property);
static lldb::OptionValuePropertiesSP
GetSettingForStructuredDataPlugin(Debugger &debugger,
const ConstString &setting_name);
static bool
CreateSettingForStructuredDataPlugin(Debugger &debugger,
const lldb::OptionValuePropertiesSP &properties_sp,
const ConstString &description,
bool is_global_property);
};
} // namespace lldb_private

View File

@ -451,6 +451,17 @@ public:
LongestCommonPrefix (std::string &common_prefix);
//------------------------------------------------------------------
/// Add or replace an environment variable with the given value.
///
/// This command adds the environment variable if it is not already
/// present using the given value. If the environment variable is
/// already in the list, it replaces the first such occurrence
/// with the new value.
//------------------------------------------------------------------
void
AddOrReplaceEnvironmentVariable(const char *env_var_name,
const char *new_value);
/// Return whether a given environment variable exists.
///
/// This command treats Args like a list of environment variables,
@ -460,12 +471,18 @@ public:
/// @param[in] env_var_name
/// Specifies the name of the environment variable to check.
///
/// @param[out] argument_index
/// If non-null, then when the environment variable is found,
/// the index of the argument position will be returned in
/// the size_t pointed to by this argument.
///
/// @return
/// true if the specified env var name exists in the list in
/// either of the above-mentioned formats; otherwise, false.
//------------------------------------------------------------------
bool
ContainsEnvironmentVariable(const char *env_var_name) const;
ContainsEnvironmentVariable(const char *env_var_name,
size_t *argument_index = nullptr) const;
protected:
//------------------------------------------------------------------

View File

@ -760,7 +760,8 @@ public:
eBroadcastBitInterrupt = (1 << 1),
eBroadcastBitSTDOUT = (1 << 2),
eBroadcastBitSTDERR = (1 << 3),
eBroadcastBitProfileData = (1 << 4)
eBroadcastBitProfileData = (1 << 4),
eBroadcastBitStructuredData = (1 << 5),
};
enum
@ -3254,6 +3255,71 @@ public:
AdvanceAddressToNextBranchInstruction (Address default_stop_addr,
AddressRange range_bounds);
//------------------------------------------------------------------
/// Configure asynchronous structured data feature.
///
/// Each Process type that supports using an asynchronous StructuredData
/// feature should implement this to enable/disable/configure the feature.
/// The default implementation here will always return an error indiciating
/// the feature is unsupported.
///
/// StructuredDataPlugin implementations will call this to configure
/// a feature that has been reported as being supported.
///
/// @param[in] type_name
/// The StructuredData type name as previously discovered by
/// the Process-derived instance.
///
/// @param[in] config
/// Configuration data for the feature being enabled. This config
/// data, which may be null, will be passed along to the feature
/// to process. The feature will dictate whether this is a dictionary,
/// an array or some other object. If the feature needs to be
/// set up properly before it can be enabled, then the config should
/// also take an enable/disable flag.
///
/// @return
/// Returns the result of attempting to configure the feature.
//------------------------------------------------------------------
virtual Error
ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp);
//------------------------------------------------------------------
/// Broadcasts the given structured data object from the given
/// plugin.
///
/// StructuredDataPlugin instances can use this to optionally
/// broadcast any of their data if they want to make it available
/// for clients. The data will come in on the structured data
/// event bit (eBroadcastBitStructuredData).
///
/// @param[in] object_sp
/// The structured data object to broadcast.
///
/// @param[in] plugin_sp
/// The plugin that will be reported in the event's plugin
/// parameter.
//------------------------------------------------------------------
void
BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
const lldb::StructuredDataPluginSP &plugin_sp);
//------------------------------------------------------------------
/// Returns the StructuredDataPlugin associated with a given type
/// name, if there is one.
///
/// There will only be a plugin for a given StructuredDataType if the
/// debugged process monitor claims that the feature is supported.
/// This is one way to tell whether a feature is available.
///
/// @return
/// The plugin if one is available for the specified feature;
/// otherwise, returns an empty shared pointer.
//------------------------------------------------------------------
lldb::StructuredDataPluginSP
GetStructuredDataPlugin(const ConstString &type_name) const;
protected:
void
SetState (lldb::EventSP &event_sp);
@ -3391,6 +3457,57 @@ protected:
m_force_next_event_delivery = true;
}
//------------------------------------------------------------------
/// Loads any plugins associated with asynchronous structured data
/// and maps the relevant supported type name to the plugin.
///
/// Processes can receive asynchronous structured data from the
/// process monitor. This method will load and map any structured
/// data plugins that support the given set of supported type names.
/// Later, if any of these features are enabled, the process monitor
/// is free to generate asynchronous structured data. The data must
/// come in as a single \b StructuredData::Dictionary. That dictionary
/// must have a string field named 'type', with a value that equals
/// the relevant type name string (one of the values in
/// \b supported_type_names).
///
/// @param[in] supported_type_names
/// An array of zero or more type names. Each must be unique.
/// For each entry in the list, a StructuredDataPlugin will be
/// searched for that supports the structured data type name.
//------------------------------------------------------------------
void
MapSupportedStructuredDataPlugins(const StructuredData::Array
&supported_type_names);
//------------------------------------------------------------------
/// Route the incoming structured data dictionary to the right plugin.
///
/// The incoming structured data must be a dictionary, and it must
/// have a key named 'type' that stores a string value. The string
/// value must be the name of the structured data feature that
/// knows how to handle it.
///
/// @param[in] object_sp
/// When non-null and pointing to a dictionary, the 'type'
/// key's string value is used to look up the plugin that
/// was registered for that structured data type. It then
/// calls the following method on the StructuredDataPlugin
/// instance:
///
/// virtual void
/// HandleArrivalOfStructuredData(Process &process,
/// const ConstString &type_name,
/// const StructuredData::ObjectSP
/// &object_sp)
///
/// @return
/// True if the structured data was routed to a plugin; otherwise,
/// false.
//------------------------------------------------------------------
bool
RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp);
//------------------------------------------------------------------
// Type definitions
//------------------------------------------------------------------
@ -3408,6 +3525,9 @@ protected:
{
}
};
using StructuredDataPluginMap = std::map<ConstString,
lldb::StructuredDataPluginSP>;
//------------------------------------------------------------------
// Member variables
@ -3477,7 +3597,9 @@ protected:
bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, don't support the ability to modify the stack.
WarningsCollection m_warnings_issued; // A set of object pointers which have already had warnings printed
std::mutex m_run_thread_plan_lock;
StructuredDataPluginMap m_structured_data_plugin_map;
enum {
eCanJITDontKnow= 0,
eCanJITYes,
@ -3562,7 +3684,7 @@ protected:
void
BroadcastAsyncProfileData(const std::string &one_profile_data);
static void
STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len);

View File

@ -0,0 +1,204 @@
//===-- StructuredDataPlugin.h ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef StructuredDataPlugin_h
#define StructuredDataPlugin_h
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/StructuredData.h"
namespace lldb_private
{
class CommandObjectMultiword;
// -----------------------------------------------------------------------------
/// Plugin that supports process-related structured data sent asynchronously
/// from the debug monitor (e.g. debugserver, lldb-server, etc.)
///
/// This plugin type is activated by a Process-derived instance when that
/// instance detects that a given structured data feature is available.
///
/// StructuredDataPlugin instances are inherently tied to a process. The
/// main functionality they support is the ability to consume asynchronously-
/// delivered structured data from the process monitor, and do something
/// reasonable with it. Something reasonable can include broadcasting a
/// StructuredData event, which other parts of the system can then do with
/// as they please. An IDE could use this facility to retrieve CPU usage,
/// memory usage, and other run-time aspects of the process. That data
/// can then be displayed meaningfully to the user through the IDE.
/// For command-line LLDB, the Debugger instance listens for the structured
/// data events raised by the plugin, and give the plugin both the output
/// and error streams such that the plugin can display something about the
/// event, at a time when the debugger ensures it is safe to write to the
/// output or error streams.
// -----------------------------------------------------------------------------
class StructuredDataPlugin :
public PluginInterface,
public std::enable_shared_from_this<StructuredDataPlugin>
{
public:
virtual ~StructuredDataPlugin();
lldb::ProcessSP
GetProcess() const;
// -------------------------------------------------------------------------
// Public instance API
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
/// Return whether this plugin supports the given StructuredData feature.
///
/// When Process is informed of a list of process-monitor-supported
/// structured data features, Process will go through the list of plugins,
/// one at a time, and have the first plugin that supports a given feature
/// be the plugin instantiated to handle that feature. There is a 1-1
/// correspondence between a Process instance and a StructuredDataPlugin
/// mapped to that process. A plugin can support handling multiple
/// features, and if that happens, there is a single plugin instance
/// created covering all of the mapped features for a given process.
///
/// @param[in] type_name
/// The name of the feature tag supported by a process.
/// e.g. "darwin-log".
///
/// @return
/// true if the plugin supports the feature; otherwise, false.
// -------------------------------------------------------------------------
virtual bool
SupportsStructuredDataType(const ConstString &type_name) = 0;
// -------------------------------------------------------------------------
/// Handle the arrival of asynchronous structured data from the process.
///
/// When asynchronous structured data arrives from the process monitor,
/// it is immediately delivered to the plugin mapped for that feature
/// if one exists. The structured data that arrives from a process
/// monitor must be a dictionary, and it must have a string field named
/// "type" that must contain the StructuredData feature name set as the
/// value. This is the manner in which the data is routed to the proper
/// plugin instance.
///
/// @param[in] process
/// The process instance that just received the structured data.
/// This will always be the same process for a given instance of
/// a plugin.
///
/// @param[in] type_name
/// The name of the feature tag for the asynchronous structured data.
/// Note this data will also be present in the \b object_sp dictionary
/// under the string value with key "type".
///
/// @param[in] object_sp
/// A shared pointer to the structured data that arrived. This must
/// be a dictionary. The only key required is the aforementioned
/// key named "type" that must be a string value containing the
/// structured data type name.
// -------------------------------------------------------------------------
virtual void
HandleArrivalOfStructuredData(Process &process,
const ConstString &type_name,
const StructuredData::ObjectSP
&object_sp) = 0;
// -------------------------------------------------------------------------
/// Get a human-readable description of the contents of the data.
///
/// In command-line LLDB, this method will be called by the Debugger
/// instance for each structured data event generated, and the output
/// will be printed to the LLDB console. If nothing is added to the stream,
/// nothing will be printed; otherwise, a newline will be added to the end
/// when displayed.
///
/// @param[in] object_sp
/// A shared pointer to the structured data to format.
///
/// @param[in] stream
/// The stream where the structured data should be pretty printed.
///
/// @return
/// The error if formatting the object contents failed; otherwise,
/// success.
// -------------------------------------------------------------------------
virtual Error
GetDescription(const StructuredData::ObjectSP &object_sp,
lldb_private::Stream &stream) = 0;
// -------------------------------------------------------------------------
/// Returns whether the plugin's features are enabled.
///
/// This is a convenience method for plugins that can enable or disable
/// their functionality. It allows retrieval of this state without
/// requiring a cast.
///
/// @param[in] type_name
/// The name of the feature tag for the asynchronous structured data.
/// This is needed for plugins that support more than one feature.
// -------------------------------------------------------------------------
virtual bool
GetEnabled(const ConstString &type_name) const;
// -------------------------------------------------------------------------
/// Allow the plugin to do work related to modules that loaded in the
/// the corresponding process.
///
/// This method defaults to doing nothing. Plugins can override it
/// if they have any behavior they want to enable/modify based on loaded
/// modules.
///
/// @param[in] process
/// The process that just was notified of modules having been loaded.
/// This will always be the same process for a given instance of
/// a plugin.
///
/// @param[in] module_list
/// The list of modules that the process registered as having just
/// loaded. See \b Process::ModulesDidLoad(...).
// -------------------------------------------------------------------------
virtual void
ModulesDidLoad(Process &process, ModuleList &module_list);
protected:
// -------------------------------------------------------------------------
// Derived-class API
// -------------------------------------------------------------------------
StructuredDataPlugin(const lldb::ProcessWP &process_wp);
// -------------------------------------------------------------------------
/// Derived classes must call this before attempting to hook up commands
/// to the 'plugin structured-data' tree.
///
/// This ensures the relevant command and options hook points for all
/// StructuredDataPlugin derived classes are available for this debugger.
/// If this has already happened, this call is a no-op.
///
/// @param[in] debugger
/// The Debugger instance for which we're creating the required shared
/// components for the StructuredDataPlugin derived classes.
// -------------------------------------------------------------------------
static void
InitializeBasePluginForDebugger(Debugger &debugger);
private:
lldb::ProcessWP m_process_wp;
DISALLOW_COPY_AND_ASSIGN(StructuredDataPlugin);
};
}
#endif

View File

@ -0,0 +1,69 @@
//===-- ThreadPlanCallOnFunctionExit.h --------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef ThreadPlanCallOnFunctionExit_h
#define ThreadPlanCallOnFunctionExit_h
#include "lldb/Target/ThreadPlan.h"
#include <functional>
namespace lldb_private {
// =============================================================================
/// This thread plan calls a function object when the current function exits.
// =============================================================================
class ThreadPlanCallOnFunctionExit : public ThreadPlan
{
public:
/// Definition for the callback made when the currently executing thread
/// finishes executing its function.
using Callback = std::function<void()>;
ThreadPlanCallOnFunctionExit(Thread &thread, const Callback &callback);
void
DidPush() override;
// -------------------------------------------------------------------------
// ThreadPlan API
// -------------------------------------------------------------------------
void
GetDescription (Stream *s, lldb::DescriptionLevel level) override;
bool
ValidatePlan (Stream *error) override;
bool
ShouldStop (Event *event_ptr) override;
bool
WillStop () override;
protected:
bool
DoPlanExplainsStop (Event *event_ptr) override;
lldb::StateType
GetPlanRunState () override;
private:
Callback m_callback;
lldb::ThreadPlanSP m_step_out_threadplan_sp;
};
}
#endif /* ThreadPlanCallOnFunctionExit_h */

View File

@ -91,6 +91,7 @@ class Error;
class EvaluateExpressionOptions;
class Event;
class EventData;
class EventDataStructuredData;
class ExecutionContext;
class ExecutionContextRef;
class ExecutionContextRefLocker;
@ -215,6 +216,7 @@ class StreamFile;
class StreamString;
class StringList;
struct StringSummaryFormat;
class StructuredDataPlugin;
class SystemRuntime;
class TypeSummaryImpl;
class TypeSummaryOptions;
@ -333,6 +335,7 @@ namespace lldb {
typedef std::unique_ptr<lldb_private::DynamicLoader> DynamicLoaderUP;
typedef std::shared_ptr<lldb_private::Event> EventSP;
typedef std::shared_ptr<lldb_private::EventData> EventDataSP;
typedef std::shared_ptr<lldb_private::EventDataStructuredData> EventDataStructuredDataSP;
typedef std::shared_ptr<lldb_private::ExecutionContextRef> ExecutionContextRefSP;
typedef std::shared_ptr<lldb_private::ExpressionVariable> ExpressionVariableSP;
typedef std::shared_ptr<lldb_private::File> FileSP;
@ -415,6 +418,10 @@ namespace lldb {
typedef std::weak_ptr<lldb_private::Stream> StreamWP;
typedef std::shared_ptr<lldb_private::StreamFile> StreamFileSP;
typedef std::shared_ptr<lldb_private::StringSummaryFormat> StringTypeSummaryImplSP;
typedef std::shared_ptr<lldb_private::StructuredDataPlugin>
StructuredDataPluginSP;
typedef std::weak_ptr<lldb_private::StructuredDataPlugin>
StructuredDataPluginWP;
typedef std::shared_ptr<lldb_private::SymbolFile> SymbolFileSP;
typedef std::shared_ptr<lldb_private::SymbolFileType> SymbolFileTypeSP;
typedef std::weak_ptr<lldb_private::SymbolFileType> SymbolFileTypeWP;

View File

@ -35,6 +35,9 @@ namespace lldb_private
typedef Language *(*LanguageCreateInstance) (lldb::LanguageType language);
typedef LanguageRuntime *(*LanguageRuntimeCreateInstance) (Process *process, lldb::LanguageType language);
typedef lldb::CommandObjectSP (*LanguageRuntimeGetCommandObject) (CommandInterpreter& interpreter);
typedef lldb::StructuredDataPluginSP (*StructuredDataPluginCreateInstance)
(Process &process);
typedef Error (*StructuredDataFilterLaunchInfo)(ProcessLaunchInfo &launch_info, Target *target);
typedef SystemRuntime *(*SystemRuntimeCreateInstance) (Process *process);
typedef lldb::PlatformSP (*PlatformCreateInstance) (bool force, const ArchSpec *arch);
typedef lldb::ProcessSP (*ProcessCreateInstance) (lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path);

View File

@ -57,6 +57,8 @@
23059A0719532B96007B8189 /* LinuxSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23059A0519532B96007B8189 /* LinuxSignals.cpp */; };
23059A101958B319007B8189 /* SBUnixSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23059A0F1958B319007B8189 /* SBUnixSignals.cpp */; };
23059A121958B3B2007B8189 /* SBUnixSignals.h in Headers */ = {isa = PBXBuildFile; fileRef = 23059A111958B37B007B8189 /* SBUnixSignals.h */; settings = {ATTRIBUTES = (Public, ); }; };
230EC4591D63C3A7008DF59F /* CMakeLists.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 230EC4571D63C3A7008DF59F /* CMakeLists.txt */; };
230EC45B1D63C3BA008DF59F /* ThreadPlanCallOnFunctionExit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 230EC4581D63C3A7008DF59F /* ThreadPlanCallOnFunctionExit.cpp */; };
2326CF441BDD643700A5CEAC /* liblldb-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; };
2326CF491BDD67D800A5CEAC /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF471BDD67C100A5CEAC /* libncurses.dylib */; };
2326CF4B1BDD681800A5CEAC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF4A1BDD681800A5CEAC /* libz.dylib */; };
@ -72,9 +74,15 @@
233B007F1960CB280090E598 /* ProcessLaunchInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B007E1960CB280090E598 /* ProcessLaunchInfo.cpp */; };
236124A41986B4E2004EFC37 /* IOObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A21986B4E2004EFC37 /* IOObject.cpp */; };
236124A51986B4E2004EFC37 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A31986B4E2004EFC37 /* Socket.cpp */; };
2374D7461D4BAA1D005C9575 /* CMakeLists.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2374D7431D4BAA1D005C9575 /* CMakeLists.txt */; };
2374D7521D4BB299005C9575 /* GDBRemoteClientBase.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2374D74F1D4BB299005C9575 /* GDBRemoteClientBase.h */; };
2374D7531D4BB2FF005C9575 /* GDBRemoteClientBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2374D74E1D4BB299005C9575 /* GDBRemoteClientBase.cpp */; };
2377C2F819E613C100737875 /* PipePosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2377C2F719E613C100737875 /* PipePosix.cpp */; };
238F2B9E1D2C82D0001FF92A /* StructuredDataPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 238F2B9D1D2C82D0001FF92A /* StructuredDataPlugin.cpp */; };
238F2BA11D2C835A001FF92A /* StructuredDataPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 238F2B9F1D2C835A001FF92A /* StructuredDataPlugin.h */; };
238F2BA21D2C835A001FF92A /* SystemRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 238F2BA01D2C835A001FF92A /* SystemRuntime.h */; };
238F2BA81D2C85FA001FF92A /* StructuredDataDarwinLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 238F2BA61D2C85FA001FF92A /* StructuredDataDarwinLog.cpp */; };
238F2BA91D2C85FA001FF92A /* StructuredDataDarwinLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 238F2BA71D2C85FA001FF92A /* StructuredDataDarwinLog.h */; };
239481861C59EBDD00DF7168 /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 239481851C59EBDD00DF7168 /* libncurses.dylib */; };
239504DE1BDD453200963CEA /* SocketAddressTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F9391BDD332400BA9A93 /* SocketAddressTest.cpp */; };
239504DF1BDD453200963CEA /* SocketTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F93A1BDD332400BA9A93 /* SocketTest.cpp */; };
@ -93,6 +101,11 @@
23D065911D4A7BEE0008EDE6 /* RenderScriptx86ABIFixups.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D065861D4A7BDA0008EDE6 /* RenderScriptx86ABIFixups.cpp */; };
23D4007D1C2101F2000C3885 /* DWARFDebugMacro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23E77CD61C20F29F007192AD /* DWARFDebugMacro.cpp */; };
23D4007E1C210201000C3885 /* DebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23E77CDB1C20F2F2007192AD /* DebugMacros.cpp */; };
23DCBE9B1D63E14B0084C36B /* SBLanguageRuntime.i in CopyFiles */ = {isa = PBXBuildFile; fileRef = 23DCBE971D63E14B0084C36B /* SBLanguageRuntime.i */; };
23DCBE9D1D63E14B0084C36B /* SBTypeEnumMember.i in CopyFiles */ = {isa = PBXBuildFile; fileRef = 23DCBE991D63E14B0084C36B /* SBTypeEnumMember.i */; };
23DCBE9E1D63E14B0084C36B /* SBUnixSignals.i in CopyFiles */ = {isa = PBXBuildFile; fileRef = 23DCBE9A1D63E14B0084C36B /* SBUnixSignals.i */; };
23DCBEA21D63E7190084C36B /* SBStructuredData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DCBEA01D63E6440084C36B /* SBStructuredData.cpp */; };
23DCBEA31D63E71F0084C36B /* SBStructuredData.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DCBE9F1D63E3800084C36B /* SBStructuredData.h */; settings = {ATTRIBUTES = (Public, ); }; };
23DCEA461D1C4D0F00A602B4 /* SBMemoryRegionInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DCEA421D1C4C6900A602B4 /* SBMemoryRegionInfo.cpp */; };
23DCEA471D1C4D0F00A602B4 /* SBMemoryRegionInfoList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DCEA431D1C4C6900A602B4 /* SBMemoryRegionInfoList.cpp */; };
23DDF226196C3EE600BB8417 /* CommandOptionValidators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */; };
@ -1151,9 +1164,14 @@
2374D7521D4BB299005C9575 /* GDBRemoteClientBase.h in CopyFiles */,
23D0658C1D4A7BDA0008EDE6 /* RenderScriptRuntime.h in CopyFiles */,
23D0658A1D4A7BDA0008EDE6 /* RenderScriptExpressionOpts.h in CopyFiles */,
230EC4591D63C3A7008DF59F /* CMakeLists.txt in CopyFiles */,
49DEF1221CD7BD90006A7C7D /* BlockPointer.h in CopyFiles */,
2374D7461D4BAA1D005C9575 /* CMakeLists.txt in CopyFiles */,
4CC7C6531D5299140076FF94 /* DWARFASTParserOCaml.h in CopyFiles */,
23DCBE9D1D63E14B0084C36B /* SBTypeEnumMember.i in CopyFiles */,
AF90106515AB7D3600FF120D /* lldb.1 in CopyFiles */,
23DCBE9B1D63E14B0084C36B /* SBLanguageRuntime.i in CopyFiles */,
23DCBE9E1D63E14B0084C36B /* SBUnixSignals.i in CopyFiles */,
23D065881D4A7BDA0008EDE6 /* CMakeLists.txt in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
@ -1181,6 +1199,8 @@
23059A0619532B96007B8189 /* LinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxSignals.h; path = Utility/LinuxSignals.h; sourceTree = "<group>"; };
23059A0F1958B319007B8189 /* SBUnixSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBUnixSignals.cpp; path = source/API/SBUnixSignals.cpp; sourceTree = "<group>"; };
23059A111958B37B007B8189 /* SBUnixSignals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBUnixSignals.h; path = include/lldb/API/SBUnixSignals.h; sourceTree = "<group>"; };
230EC4571D63C3A7008DF59F /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = source/Target/CMakeLists.txt; sourceTree = "<group>"; };
230EC4581D63C3A7008DF59F /* ThreadPlanCallOnFunctionExit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallOnFunctionExit.cpp; path = source/Target/ThreadPlanCallOnFunctionExit.cpp; sourceTree = "<group>"; };
23173F8B192BA93F005C708F /* lldb-x86-register-enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-x86-register-enums.h"; path = "Utility/lldb-x86-register-enums.h"; sourceTree = "<group>"; };
2321F9381BDD332400BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
2321F9391BDD332400BA9A93 /* SocketAddressTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SocketAddressTest.cpp; sourceTree = "<group>"; };
@ -1221,10 +1241,16 @@
236124A31986B4E2004EFC37 /* Socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Socket.cpp; sourceTree = "<group>"; };
236124A61986B50E004EFC37 /* IOObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = IOObject.h; path = include/lldb/Host/IOObject.h; sourceTree = "<group>"; };
236124A71986B50E004EFC37 /* Socket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Socket.h; path = include/lldb/Host/Socket.h; sourceTree = "<group>"; };
2374D7431D4BAA1D005C9575 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
2374D74E1D4BB299005C9575 /* GDBRemoteClientBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteClientBase.cpp; sourceTree = "<group>"; };
2374D74F1D4BB299005C9575 /* GDBRemoteClientBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GDBRemoteClientBase.h; sourceTree = "<group>"; };
2377C2F719E613C100737875 /* PipePosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PipePosix.cpp; sourceTree = "<group>"; };
237C577A19AF9D9F00213D59 /* HostInfoLinux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoLinux.h; path = include/lldb/Host/linux/HostInfoLinux.h; sourceTree = SOURCE_ROOT; };
238F2B9D1D2C82D0001FF92A /* StructuredDataPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StructuredDataPlugin.cpp; path = source/Target/StructuredDataPlugin.cpp; sourceTree = "<group>"; };
238F2B9F1D2C835A001FF92A /* StructuredDataPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StructuredDataPlugin.h; path = include/lldb/Target/StructuredDataPlugin.h; sourceTree = "<group>"; };
238F2BA01D2C835A001FF92A /* SystemRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemRuntime.h; path = include/lldb/Target/SystemRuntime.h; sourceTree = "<group>"; };
238F2BA61D2C85FA001FF92A /* StructuredDataDarwinLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StructuredDataDarwinLog.cpp; sourceTree = "<group>"; };
238F2BA71D2C85FA001FF92A /* StructuredDataDarwinLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructuredDataDarwinLog.h; sourceTree = "<group>"; };
239481851C59EBDD00DF7168 /* libncurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.dylib; path = ../../../../../usr/lib/libncurses.dylib; sourceTree = "<group>"; };
239504C21BDD3FD600963CEA /* gtest_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gtest_common.h; sourceTree = "<group>"; };
239504C61BDD3FF300963CEA /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
@ -1242,6 +1268,12 @@
23D065851D4A7BDA0008EDE6 /* RenderScriptRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderScriptRuntime.h; sourceTree = "<group>"; };
23D065861D4A7BDA0008EDE6 /* RenderScriptx86ABIFixups.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderScriptx86ABIFixups.cpp; sourceTree = "<group>"; };
23D065871D4A7BDA0008EDE6 /* RenderScriptx86ABIFixups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderScriptx86ABIFixups.h; sourceTree = "<group>"; };
23DCBE971D63E14B0084C36B /* SBLanguageRuntime.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBLanguageRuntime.i; sourceTree = "<group>"; };
23DCBE981D63E14B0084C36B /* SBStructuredData.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBStructuredData.i; sourceTree = "<group>"; };
23DCBE991D63E14B0084C36B /* SBTypeEnumMember.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeEnumMember.i; sourceTree = "<group>"; };
23DCBE9A1D63E14B0084C36B /* SBUnixSignals.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBUnixSignals.i; sourceTree = "<group>"; };
23DCBE9F1D63E3800084C36B /* SBStructuredData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBStructuredData.h; path = include/lldb/API/SBStructuredData.h; sourceTree = "<group>"; };
23DCBEA01D63E6440084C36B /* SBStructuredData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBStructuredData.cpp; path = source/API/SBStructuredData.cpp; sourceTree = "<group>"; };
23DCEA421D1C4C6900A602B4 /* SBMemoryRegionInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBMemoryRegionInfo.cpp; path = source/API/SBMemoryRegionInfo.cpp; sourceTree = "<group>"; };
23DCEA431D1C4C6900A602B4 /* SBMemoryRegionInfoList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBMemoryRegionInfoList.cpp; path = source/API/SBMemoryRegionInfoList.cpp; sourceTree = "<group>"; };
23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandOptionValidators.cpp; path = source/Interpreter/CommandOptionValidators.cpp; sourceTree = "<group>"; };
@ -3235,6 +3267,23 @@
path = platforms;
sourceTree = "<group>";
};
238F2BA41D2C858F001FF92A /* StructuredData */ = {
isa = PBXGroup;
children = (
238F2BA51D2C85B2001FF92A /* DarwinLog */,
);
path = StructuredData;
sourceTree = "<group>";
};
238F2BA51D2C85B2001FF92A /* DarwinLog */ = {
isa = PBXGroup;
children = (
238F2BA71D2C85FA001FF92A /* StructuredDataDarwinLog.h */,
238F2BA61D2C85FA001FF92A /* StructuredDataDarwinLog.cpp */,
);
path = DarwinLog;
sourceTree = "<group>";
};
23AB0526199FF5D3003B8084 /* FreeBSD */ = {
isa = PBXGroup;
children = (
@ -3279,6 +3328,7 @@
26C5577E132575B6008FD8FE /* Platform */,
260C898A10F57C5600BB2B04 /* Process */,
3FBA69DA1B6066D20008F44A /* ScriptInterpreter */,
238F2BA41D2C858F001FF92A /* StructuredData */,
AF11CB34182CA85A00D9B618 /* SystemRuntime */,
260C89B110F57C5600BB2B04 /* SymbolFile */,
260C89E010F57C5600BB2B04 /* SymbolVendor */,
@ -3508,9 +3558,8 @@
2611FEEE142D83060017FEA3 /* interface */ = {
isa = PBXGroup;
children = (
254FBBA61A91672800BD6378 /* SBAttachInfo.i */,
254FBB921A81AA5200BD6378 /* SBLaunchInfo.i */,
2611FEEF142D83060017FEA3 /* SBAddress.i */,
254FBBA61A91672800BD6378 /* SBAttachInfo.i */,
2611FEF0142D83060017FEA3 /* SBBlock.i */,
2611FEF1142D83060017FEA3 /* SBBreakpoint.i */,
2611FEF2142D83060017FEA3 /* SBBreakpointLocation.i */,
@ -3533,6 +3582,8 @@
2611FF00142D83060017FEA3 /* SBHostOS.i */,
2611FF02142D83060017FEA3 /* SBInstruction.i */,
2611FF03142D83060017FEA3 /* SBInstructionList.i */,
23DCBE971D63E14B0084C36B /* SBLanguageRuntime.i */,
254FBB921A81AA5200BD6378 /* SBLaunchInfo.i */,
2611FF04142D83060017FEA3 /* SBLineEntry.i */,
2611FF05142D83060017FEA3 /* SBListener.i */,
264297591D1DF2AA003F2BF4 /* SBMemoryRegionInfo.i */,
@ -3547,6 +3598,7 @@
2611FF09142D83060017FEA3 /* SBSourceManager.i */,
2611FF0A142D83060017FEA3 /* SBStream.i */,
2611FF0B142D83060017FEA3 /* SBStringList.i */,
23DCBE981D63E14B0084C36B /* SBStructuredData.i */,
2611FF0C142D83060017FEA3 /* SBSymbol.i */,
2611FF0D142D83060017FEA3 /* SBSymbolContext.i */,
2611FF0E142D83060017FEA3 /* SBSymbolContextList.i */,
@ -3556,11 +3608,13 @@
8CCB018419BA54930009FD44 /* SBThreadCollection.i */,
2611FF11142D83060017FEA3 /* SBType.i */,
9475C18A14E5EA1C001BFC6D /* SBTypeCategory.i */,
23DCBE991D63E14B0084C36B /* SBTypeEnumMember.i */,
9461569214E3567F003A195C /* SBTypeFilter.i */,
9461569314E3567F003A195C /* SBTypeFormat.i */,
9475C18B14E5F818001BFC6D /* SBTypeNameSpecifier.i */,
9461569414E3567F003A195C /* SBTypeSummary.i */,
9461569514E3567F003A195C /* SBTypeSynthetic.i */,
23DCBE9A1D63E14B0084C36B /* SBUnixSignals.i */,
2611FF12142D83060017FEA3 /* SBValue.i */,
2611FF13142D83060017FEA3 /* SBValueList.i */,
94235B9D1A8D601A00EB2EED /* SBVariablesOptions.i */,
@ -3671,6 +3725,8 @@
26C72C951243229A0068DC16 /* SBStream.cpp */,
9A357670116E7B5200E8ED2F /* SBStringList.h */,
9A357672116E7B6400E8ED2F /* SBStringList.cpp */,
23DCBE9F1D63E3800084C36B /* SBStructuredData.h */,
23DCBEA01D63E6440084C36B /* SBStructuredData.cpp */,
26DE205A11618FF600A093E2 /* SBSymbol.h */,
26DE20641161904E00A093E2 /* SBSymbol.cpp */,
26DE204011618AB900A093E2 /* SBSymbolContext.h */,
@ -4911,6 +4967,7 @@
26BC7DEF10F1B80200F91463 /* Target */ = {
isa = PBXGroup;
children = (
230EC4571D63C3A7008DF59F /* CMakeLists.txt */,
8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */,
8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */,
8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */,
@ -4974,6 +5031,9 @@
26BC7F3A10F1B90C00F91463 /* StackID.cpp */,
2615DB841208A9C90021781D /* StopInfo.h */,
2615DB861208A9E40021781D /* StopInfo.cpp */,
238F2B9F1D2C835A001FF92A /* StructuredDataPlugin.h */,
238F2B9D1D2C82D0001FF92A /* StructuredDataPlugin.cpp */,
238F2BA01D2C835A001FF92A /* SystemRuntime.h */,
AF81DEF91828A23F0042CF19 /* SystemRuntime.cpp */,
26BC7DF810F1B81A00F91463 /* Target.h */,
26BC7F3B10F1B90C00F91463 /* Target.cpp */,
@ -4993,6 +5053,7 @@
49EC3E98118F90AC00B1265E /* ThreadPlanCallFunction.cpp */,
EB8375E81B553DFE00BA907D /* ThreadPlanCallFunctionUsingABI.h */,
EB8375E61B553DE800BA907D /* ThreadPlanCallFunctionUsingABI.cpp */,
230EC4581D63C3A7008DF59F /* ThreadPlanCallOnFunctionExit.cpp */,
4C7CF7E31295E10E00B4FBB5 /* ThreadPlanCallUserExpression.h */,
4C7CF7E51295E12B00B4FBB5 /* ThreadPlanCallUserExpression.cpp */,
4C56543219D1EFB5002E9C44 /* ThreadPlanPython.h */,
@ -5541,6 +5602,7 @@
4CEE62F71145F1C70064CF93 /* GDB Remote */ = {
isa = PBXGroup;
children = (
2374D7431D4BAA1D005C9575 /* CMakeLists.txt */,
2374D74F1D4BB299005C9575 /* GDBRemoteClientBase.h */,
2374D74E1D4BB299005C9575 /* GDBRemoteClientBase.cpp */,
6D55B2931A8A808400A70529 /* GDBRemoteCommunicationServerCommon.h */,
@ -6077,6 +6139,7 @@
9A357671116E7B5200E8ED2F /* SBStringList.h in Headers */,
26DE205B11618FF600A093E2 /* SBSymbol.h in Headers */,
262F12B71835469C00AEB384 /* SBPlatform.h in Headers */,
23DCBEA31D63E71F0084C36B /* SBStructuredData.h in Headers */,
26DE204111618AB900A093E2 /* SBSymbolContext.h in Headers */,
268F9D53123AA15200B91E9B /* SBSymbolContextList.h in Headers */,
2668022C115FD13D008E1FE4 /* SBTarget.h in Headers */,
@ -6115,9 +6178,12 @@
4984BA181B979C08008658D4 /* ExpressionVariable.h in Headers */,
26C7C4841BFFEA7E009BD01F /* WindowsMiniDump.h in Headers */,
30B38A001CAAA6D7009524E3 /* ClangUtil.h in Headers */,
238F2BA11D2C835A001FF92A /* StructuredDataPlugin.h in Headers */,
AF8AD62F1BEC28A400150209 /* PlatformAppleTVSimulator.h in Headers */,
238F2BA91D2C85FA001FF92A /* StructuredDataDarwinLog.h in Headers */,
AF8AD63A1BEC28C400150209 /* PlatformRemoteAppleWatch.h in Headers */,
257906651BD5AFD000178368 /* Acceptor.h in Headers */,
238F2BA21D2C835A001FF92A /* SystemRuntime.h in Headers */,
260A63171861008E00FECF8E /* IOHandler.h in Headers */,
267F68581CC02EAE0086832B /* RegisterContextPOSIX_s390x.h in Headers */,
6D0F614F1C80AB0C00A4ECEE /* JavaLanguageRuntime.h in Headers */,
@ -6580,6 +6646,7 @@
9461569D14E358A6003A195C /* SBTypeSynthetic.cpp in Sources */,
26680324116005D9008E1FE4 /* SBThread.cpp in Sources */,
26680326116005DB008E1FE4 /* SBTarget.cpp in Sources */,
23DCBEA21D63E7190084C36B /* SBStructuredData.cpp in Sources */,
26680327116005DC008E1FE4 /* SBSourceManager.cpp in Sources */,
3F81692C1ABB7A1E001DA9DF /* SystemInitializerFull.cpp in Sources */,
26680328116005DE008E1FE4 /* SBProcess.cpp in Sources */,
@ -6928,6 +6995,7 @@
268900C213353E5F00698AC0 /* DWARFDebugPubnamesSet.cpp in Sources */,
268900C313353E5F00698AC0 /* DWARFDebugRanges.cpp in Sources */,
25EF23781AC09B3700908DF0 /* AdbClient.cpp in Sources */,
238F2BA81D2C85FA001FF92A /* StructuredDataDarwinLog.cpp in Sources */,
94380B8219940B0A00BFE4A8 /* StringLexer.cpp in Sources */,
268900C413353E5F00698AC0 /* DWARFDefines.cpp in Sources */,
945563101BEAD0650073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.mm in Sources */,
@ -7080,6 +7148,7 @@
26D5E163135BB054006EA0A7 /* OptionGroupPlatform.cpp in Sources */,
94CD131A19BA33B400DB7BED /* TypeValidator.cpp in Sources */,
26BD407F135D2AE000237D80 /* FileLineResolver.cpp in Sources */,
230EC45B1D63C3BA008DF59F /* ThreadPlanCallOnFunctionExit.cpp in Sources */,
AE44FB311BB07EB80033EB62 /* GoLexer.cpp in Sources */,
26A7A035135E6E4200FB369E /* OptionValue.cpp in Sources */,
9A22A161135E30370024DDC3 /* EmulateInstructionARM.cpp in Sources */,
@ -7251,6 +7320,7 @@
945261C81B9A14D300BF138D /* CXXFunctionPointer.cpp in Sources */,
94CB256716B096F10059775D /* TypeCategoryMap.cpp in Sources */,
94CB257016B0A4270059775D /* TypeFormat.cpp in Sources */,
238F2B9E1D2C82D0001FF92A /* StructuredDataPlugin.cpp in Sources */,
94CB257116B0A4270059775D /* TypeSummary.cpp in Sources */,
94CB257216B0A4270059775D /* TypeSynthetic.cpp in Sources */,
94CB257416B1D3880059775D /* FormatCache.cpp in Sources */,

View File

@ -0,0 +1,430 @@
"""
Base class for DarwinLog tests.
"""
# System imports
from __future__ import print_function
import json
import os
import pexpect
import platform
import re
import sys
import threading
# lldb imports
import lldb
from lldb import SBProcess, SBTarget
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import lldbtest_config
from lldbsuite.test import lldbutil
def expand_darwinlog_command(command):
return "plugin structured-data darwin-log " + command
def expand_darwinlog_settings_set_command(command):
return "settings set plugin.structured-data.darwin-log." + command
class DarwinLogTestBase(lldbtest.TestBase):
"""Base class for DarwinLog test cases that are pexpect-based."""
NO_DEBUG_INFO_TESTCASE = True
CONTINUE_REGEX = re.compile(r"Process \d+ resuming")
def setUp(self):
# Call super's setUp().
super(DarwinLogTestBase, self).setUp()
# Until other systems support this, exit
# early if we're not macOS version 10.12
# or greater.
version = platform.mac_ver()[0].split('.')
if ((int(version[0]) == 10) and (int(version[1]) < 12) or
(int(version[0]) < 10)):
self.skipTest("DarwinLog tests currently require macOS 10.12+")
return
self.child = None
self.child_prompt = '(lldb) '
self.strict_sources = False
self.enable_process_monitor_logging = False
def run_lldb_to_breakpoint(self, exe, source_file, line,
enable_command=None, settings_commands=None):
# Set self.child_prompt, which is "(lldb) ".
prompt = self.child_prompt
# So that the child gets torn down after the test.
self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec,
self.lldbOption, exe))
child = self.child
# Turn on logging for what the child sends back.
if self.TraceOn():
child.logfile_read = sys.stdout
if self.enable_process_monitor_logging:
if platform.system() == 'Darwin':
self.runCmd("settings set target.process.extra-startup-command "
"QSetLogging:bitmask=LOG_DARWIN_LOG;")
self.expect_prompt()
# Run the enable command if we have one.
if enable_command is not None:
self.runCmd(enable_command)
self.expect_prompt()
# Disable showing of source lines at our breakpoint.
# This is necessary for the logging tests, because the very
# text we want to match for output from the running inferior
# will show up in the source as well. We don't want the source
# output to erroneously make a match with our expected output.
self.runCmd("settings set stop-line-count-before 0")
self.expect_prompt()
self.runCmd("settings set stop-line-count-after 0")
self.expect_prompt()
# While we're debugging, turn on packet logging.
self.runCmd("log enable -f /tmp/packets.log gdb-remote packets")
self.expect_prompt()
# Prevent mirroring of NSLog/os_log content to stderr. We want log
# messages to come exclusively through our log channel.
self.runCmd("settings set target.env-vars IDE_DISABLED_OS_ACTIVITY_DT_MODE=1")
self.expect_prompt()
# Run any darwin-log settings commands now, before we enable logging.
if settings_commands is not None:
for setting_command in settings_commands:
self.runCmd(
expand_darwinlog_settings_set_command(setting_command))
self.expect_prompt()
# Set breakpoint right before the os_log() macros. We don't
# set it on the os_log*() calls because these are a number of
# nested-scoped calls that will cause the debugger to stop
# multiple times on the same line. That is difficult to match
# os_log() content by since it is non-deterministic what the
# ordering between stops and log lines will be. This is why
# we stop before, and then have the process run in a sleep
# afterwards, so we get the log messages while the target
# process is "running" (sleeping).
child.sendline('breakpoint set -f %s -l %d' % (source_file, line))
child.expect_exact(prompt)
# Now run to the breakpoint that we just set.
child.sendline('run')
child.expect_exact(prompt)
# Ensure we stopped at a breakpoint.
self.runCmd("thread list")
self.expect(re.compile(r"stop reason = breakpoint"))
# Now we're ready to check if DarwinLog is available.
if not self.darwin_log_available():
self.skipTest("DarwinLog not available")
def runCmd(self, cmd):
self.child.sendline(cmd)
def expect_prompt(self, exactly=True):
self.expect(self.child_prompt, exactly=exactly)
def expect(self, pattern, exactly=False, *args, **kwargs):
if exactly:
return self.child.expect_exact(pattern, *args, **kwargs)
return self.child.expect(pattern, *args, **kwargs)
def darwin_log_available(self):
self.runCmd("plugin structured-data darwin-log status")
self.expect(re.compile(r"Availability: ([\S]+)"))
return self.child.match is not None and (
self.child.match.group(1) == "available")
def do_test(self, enable_options, expect_regexes=None,
settings_commands=None):
"""Test that a single fall-through reject rule rejects all logging."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
# Build the darwin-log enable command.
enable_cmd = expand_darwinlog_command('enable')
if enable_options is not None and len(enable_options) > 0:
enable_cmd += ' ' + ' '.join(enable_options)
exe = os.path.join(os.getcwd(), self.exe_name)
self.run_lldb_to_breakpoint(exe, self.source, self.line,
enable_command=enable_cmd,
settings_commands=settings_commands)
self.expect_prompt()
# Now go.
self.runCmd("process continue")
self.expect(self.CONTINUE_REGEX)
if expect_regexes is None:
# Expect matching a log line or program exit.
# Test methods determine which ones are valid.
expect_regexes = (
[re.compile(r"source-log-([^-]+)-(\S+)"),
re.compile(r"exited with status")
])
self.expect(expect_regexes)
def remove_add_mode_entry(log_entries):
"""libtrace creates an "Add Mode:..." message when logging is enabled.
Strip this out of results since our test subjects don't create it."""
return [entry for entry in log_entries
if "message" in entry and
not entry["message"].startswith("Add Mode:")]
class DarwinLogEventBasedTestBase(lldbtest.TestBase):
"""Base class for event-based DarwinLog tests."""
NO_DEBUG_INFO_TESTCASE = True
class EventListenerThread(threading.Thread):
def __init__(self, listener, process, trace_on, max_entry_count):
super(DarwinLogEventBasedTestBase.EventListenerThread, self).__init__()
self.process = process
self.listener = listener
self.trace_on = trace_on
self.max_entry_count = max_entry_count
self.exception = None
self.structured_data_event_count = 0
self.wait_seconds = 2
self.max_timeout_count = 4
self.log_entries = []
def handle_structured_data_event(self, event):
structured_data = SBProcess.GetStructuredDataFromEvent(event)
if not structured_data.IsValid():
if self.trace_on:
print("invalid structured data")
return
# Track that we received a valid structured data event.
self.structured_data_event_count += 1
# Grab the individual log entries from the JSON.
stream = lldb.SBStream()
structured_data.GetAsJSON(stream)
dict = json.loads(stream.GetData())
self.log_entries.extend(dict["events"])
if self.trace_on:
print("Structured data (raw):", stream.GetData())
# Print the pretty-printed version.
if self.trace_on:
stream.Clear()
structured_data.PrettyPrint(stream)
print("Structured data (pretty print):",
stream.GetData())
def done(self, timeout_count):
"""Returns True when we're done listening for events."""
# See if we should consider the number of events retrieved.
if self.max_entry_count is not None:
if len(self.log_entries) >= self.max_entry_count:
# We've received the max threshold of events expected,
# we can exit here.
if self.trace_on:
print("Event listener thread exiting due to max "
"expected log entry count being reached.")
return True
# If our event timeout count has exceeded our maximum timeout count,
# we're done.
if timeout_count >= self.max_timeout_count:
if self.trace_on:
print("Event listener thread exiting due to max number of "
"WaitForEvent() timeouts being reached.")
return True
# If our process is dead, we're done.
if not self.process.is_alive:
if self.trace_on:
print("Event listener thread exiting due to test inferior "
"exiting.")
return True
# We're not done.
return False
def run(self):
event = lldb.SBEvent()
try:
timeout_count = 0
# Wait up to 4 times for the event to arrive.
while not self.done(timeout_count):
if self.trace_on:
print("Calling wait for event...")
if self.listener.WaitForEvent(self.wait_seconds, event):
while event.IsValid():
# Check if it's a process event.
if SBProcess.EventIsStructuredDataEvent(event):
self.handle_structured_data_event(event)
else:
if self.trace_on:
print("ignoring unexpected event:",
lldbutil.get_description(event))
# Grab the next event, if there is one.
event.Clear()
if not self.listener.GetNextEvent(event):
if self.trace_on:
print("listener has no more events "
"available at this time")
else:
if self.trace_on:
print("timeout occurred waiting for event...")
timeout_count += 1
self.listener.Clear()
except Exception as e:
self.exception = e
def setUp(self):
# Call super's setUp().
super(DarwinLogEventBasedTestBase, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
# Enable debugserver logging of the darwin log collection
# mechanism.
self.runCmd("settings set target.process.extra-startup-command "
"QSetLogging:bitmask=LOG_DARWIN_LOG;")
def do_test(self, enable_options, settings_commands=None,
run_enable_after_breakpoint=False, max_entry_count=None):
"""Runs the test inferior, returning collected events.
This method runs the test inferior to the first breakpoint hit.
It then adds a listener for structured data events, and collects
all events from that point forward until end of execution of the
test inferior. It then returns those events.
@return
A list of structured data events received, in the order they
were received.
"""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
exe = os.path.join(os.getcwd(), self.exe_name)
# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, lldbtest.VALID_TARGET)
# Run the darwin-log settings commands.
if settings_commands is not None:
for setting_command in settings_commands:
self.runCmd(
expand_darwinlog_settings_set_command(setting_command))
# Build the darwin-log enable command.
enable_cmd = expand_darwinlog_command("enable")
if enable_options is not None and len(enable_options) > 0:
enable_cmd += ' ' + ' '.join(enable_options)
# Run the darwin-log enable command now if we are not supposed
# to do it at the first breakpoint. This tests the start-up
# code, which has the benefit of being able to set os_log-related
# environment variables.
if not run_enable_after_breakpoint:
self.runCmd(enable_cmd)
# Create the breakpoint.
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
self.assertIsNotNone(breakpoint)
self.assertTrue(breakpoint.IsValid())
self.assertEqual(1, breakpoint.GetNumLocations(),
"Should have found one breakpoint")
# Enable packet logging.
# self.runCmd("log enable -f /tmp/packets.log gdb-remote packets")
# self.runCmd("log enable lldb process")
# Launch the process - doesn't stop at entry.
process = target.LaunchSimple(None, None, os.getcwd())
self.assertIsNotNone(process, lldbtest.PROCESS_IS_VALID)
# Keep track of whether we're tracing output.
trace_on = self.TraceOn()
# Get the next thread that stops.
from lldbsuite.test.lldbutil import get_stopped_thread
thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
self.assertIsNotNone(thread, "There should be a thread stopped "
"due to breakpoint")
# The process should be stopped at this point.
self.expect("process status", lldbtest.PROCESS_STOPPED,
patterns=['Process .* stopped'])
# The stop reason of the thread should be breakpoint.
self.expect("thread list", lldbtest.STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped', 'stop reason = breakpoint'])
# And our one and only breakpoint should have been hit.
self.assertEquals(breakpoint.GetHitCount(), 1)
# Now setup the structured data listener.
#
# Grab the broadcaster for the process. We'll be attaching our
# listener to it.
broadcaster = process.GetBroadcaster()
self.assertIsNotNone(broadcaster)
listener = lldb.SBListener("SBStructuredData listener")
self.assertIsNotNone(listener)
rc = broadcaster.AddListener(listener,
lldb.SBProcess.eBroadcastBitStructuredData)
self.assertTrue(rc, "Successfully add listener to process broadcaster")
# Start the listening thread to retrieve the events.
# Bump up max entry count for the potentially included Add Mode:
# entry.
if max_entry_count is not None:
max_entry_count += 1
event_thread = self.EventListenerThread(listener, process, trace_on,
max_entry_count)
event_thread.start()
# Continue the test inferior. We should get any events after this.
process.Continue()
# Wait until the event thread terminates.
# print("main thread now waiting for event thread to receive events.")
event_thread.join()
# If the process is still alive, we kill it here.
if process.is_alive:
process.Kill()
# Fail on any exceptions that occurred during event execution.
if event_thread.exception is not None:
# Re-raise it here so it shows up as a test error.
raise event_thread
# Return the collected logging events.
return remove_add_mode_entry(event_thread.log_entries)

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,34 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
# System imports
from __future__ import print_function
# LLDB imports
from lldbsuite.test import darwin_log
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
class TestDarwinLogBasic(darwin_log.DarwinLogEventBasedTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
@decorators.add_test_categories(['pyapi'])
@decorators.skipUnlessDarwin
def test_SBStructuredData_gets_broadcasted(self):
"""Exercise SBStructuredData API."""
# Run the test.
log_entries = self.do_test(None, max_entry_count=2)
# Validate that we received our two log entries.
self.assertEqual(len(log_entries), 1,
"Expected one log entry to arrive via events.")
self.assertEqual(log_entries[0]['message'], "Hello, world",
"Log message should match expected content.")

View File

@ -0,0 +1,32 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/log.h>
#include <stdio.h>
#include "../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger = os_log_create("org.llvm.lldb.test", "basic-test");
if (!logger)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_log(logger, "Hello, world");
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,6 @@
// The number of seconds to wait at the end of the test inferior before
// exiting. This delay is needed to ensure the logging infrastructure
// has flushed out the message. If we finished before all messages were
// flushed, then the test will never see the unflushed messages, causing
// some test logic to fail.
#define FINAL_WAIT_SECONDS 5

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,117 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterMatchActivityChain(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterMatchActivityChain, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterMatchActivityChain, self).tearDown()
# ==========================================================================
# activity-chain filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_activity_chain_match(self):
"""Test that fall-through reject, accept full-match activity chain works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain match "
"parent-activity:child-activity\""])
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_chain_partial_match(self):
"""Test that fall-through reject, doesn't accept only partial match of activity-chain."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain match parent-activity:child-activity\"", # Match the second fully.
"--filter \"accept activity-chain match parent-ac\""]) # Only partially match the first.
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_chain_full_match(self):
"""Test that fall-through accept, reject match activity-chain works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity-chain match parent-activity\""])
# We should only see the second log message as we rejected the first
# via activity-chain rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_chain_second_rule(self):
"""Test that fall-through reject, accept activity-chain on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain match non-existent\"",
"--filter \"accept activity-chain match parent-activity:child-activity\""])
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the activity-chain of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,121 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterMatchActivity(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterMatchActivity, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterMatchActivity, self).tearDown()
# ==========================================================================
# activity filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_activity_match(self):
"""Test that fall-through reject, accept match activity works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity match child-activity\""]
)
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_partial_match(self):
"""Test that fall-through reject, accept match activity via partial match does not accept."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity match child-activity\"", # Fully match second message.
"--filter \"accept activity match parent-\""] # Only partially match first message.
)
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_full_match(self):
"""Test that fall-through accept, reject match activity works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity match parent-activity\""]
)
# We should only see the second log message as we rejected the first
# via activity rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_second_rule(self):
"""Test that fall-through reject, accept regex activity on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity match non-existent\"",
"--filter \"accept activity match child-activity\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the activity of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,118 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterMatchCategory(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterMatchCategory, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterMatchCategory, self).tearDown()
# ==========================================================================
# category filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_category_full_match(self):
"""Test that fall-through reject, accept match single category works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category match cat2\""]
)
# We should only see the second log message as we only accept
# that category.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_category_partial_match(self):
"""Test that fall-through reject, accept regex category via partial match works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category match cat2\"", # Fully match the second message.
"--filter \"accept category match at1\""] # Only partially match first message. Should not show up.
)
# We should only see the second log message as we only accept
# that category.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_category_full_match(self):
"""Test that fall-through accept, reject match category works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject category match cat1\""]
)
# We should only see the second log message as we rejected the first
# via category rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_category_second_rule(self):
"""Test that fall-through reject, accept match category on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category match non-existent\"",
"--filter \"accept category match cat2\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the category of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,132 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterMatchMessage(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterMatchMessage, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
self.strict_sources = True
# Turn on process monitor logging while we work out issues.
self.enable_process_monitor_logging = True
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterMatchMessage, self).tearDown()
# ==========================================================================
# category filter tests
# ==========================================================================
EXPECT_REGEXES = [
re.compile(r"log message ([^-]+)-(\S+)"),
re.compile(r"exited with status")
]
@decorators.skipUnlessDarwin
def test_filter_accept_message_full_match(self):
"""Test that fall-through reject, accept match whole message works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept message match log message sub2-cat2\""],
expect_regexes=self.EXPECT_REGEXES
)
# We should only see the second log message as we only accept
# that message contents.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_no_accept_message_partial_match(self):
"""Test that fall-through reject, match message via partial content match doesn't accept."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept message match log message sub2-cat2\"",
"--filter \"accept message match sub1-cat1\""],
expect_regexes=self.EXPECT_REGEXES
)
# We should only see the second log message as the partial match on
# the first message should not pass.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_category_full_match(self):
"""Test that fall-through accept, reject match message works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject message match log message sub1-cat1\""],
expect_regexes=self.EXPECT_REGEXES
)
# We should only see the second log message as we rejected the first
# via message contents rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_category_second_rule(self):
"""Test that fall-through reject, accept match category on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept message match non-existent\"",
"--filter \"accept message match log message sub2-cat2\""],
expect_regexes=self.EXPECT_REGEXES
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the category of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,35 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_log(logger_sub1, "log message sub%d-cat%d", 1, 1);
os_log(logger_sub2, "log message sub%d-cat%d", 2, 2);
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(1);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,118 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterMatchSubsystem(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterMatchSubsystem, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterMatchSubsystem, self).tearDown()
# ==========================================================================
# subsystem filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_subsystem_full_match(self):
"""Test that fall-through reject, accept match single subsystem works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem match org.llvm.lldb.test.sub2\""]
)
# We should only see the second log message as we only accept
# that subsystem.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_subsystem_partial_match(self):
"""Test that fall-through reject, doesn't accept match subsystem via partial-match."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem match org.llvm.lldb.test.sub2\"", # Fully match second message subsystem.
"--filter \"accept subsystem match sub1\""] # Only partially match first subsystem.
)
# We should only see the second log message as we only accept
# that subsystem.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_subsystem_full_match(self):
"""Test that fall-through accept, reject match subsystem works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject subsystem match org.llvm.lldb.test.sub1\""]
)
# We should only see the second log message as we rejected the first
# via subsystem rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_subsystem_second_rule(self):
"""Test that fall-through reject, accept match subsystem on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem match non-existent\"",
"--filter \"accept subsystem match org.llvm.lldb.test.sub2\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the subsystem of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,132 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterRegexActivityChain(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterRegexActivityChain, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterRegexActivityChain, self).tearDown()
# ==========================================================================
# activity-chain filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_activity_chain_full_match(self):
"""Test that fall-through reject, accept full-match activity chain works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain regex "
"parent-activity:child-activity\""])
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_chain_partial_match(self):
"""Test that fall-through reject, accept activity-chain via partial match works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain regex :child-activity\""])
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_chain_full_match(self):
"""Test that fall-through accept, reject activity-chain works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity-chain regex parent-activity:child-..tivity\""])
# We should only see the second log message as we rejected the first
# via activity-chain rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat1"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_chain_partial_match(self):
"""Test that fall-through accept, reject activity-chain by partial match works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity-chain regex ^p[^:]+$\""])
# We should only see the second log message as we rejected the first
# via activity-chain rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_chain_second_rule(self):
"""Test that fall-through reject, accept activity-chain on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity-chain regex non-existent\"",
"--filter \"accept activity-chain regex child-activity\""])
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the activity-chain of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,137 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterRegexActivity(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterRegexActivity, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterRegexActivity, self).tearDown()
# ==========================================================================
# activity filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_activity_full_match(self):
"""Test that fall-through reject, accept regex full-match activity works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity regex child-activity\""]
)
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_partial_match(self):
"""Test that fall-through reject, regex accept activity via partial match works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity regex child-.*\""]
)
# We should only see the second log message as we only accept
# that activity.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_full_match(self):
"""Test that fall-through accept, reject regex activity works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity regex parent-activity\""]
)
# We should only see the second log message as we rejected the first
# via activity rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_activity_partial_match(self):
"""Test that fall-through accept, reject regex activity by partial match works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject activity regex p.+-activity\""]
)
# We should only see the second log message as we rejected the first
# via activity rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_activity_second_rule(self):
"""Test that fall-through reject, accept regex activity on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept activity regex non-existent\"",
"--filter \"accept activity regex child-activity\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the activity of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,133 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterRegexCategory(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterRegexCategory, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterRegexCategory, self).tearDown()
# ==========================================================================
# category filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_category_full_match(self):
"""Test that fall-through reject, accept regex single category works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category regex cat2\""]
)
# We should only see the second log message as we only accept
# that category.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_category_partial_match(self):
"""Test that fall-through reject, accept regex category via partial match works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category regex .+2\""]
)
# We should only see the second log message as we only accept
# that category.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_category_full_match(self):
"""Test that fall-through accept, reject regex category works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject category regex cat1\""]
)
# We should only see the second log message as we rejected the first
# via category rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_category_partial_match(self):
"""Test that fall-through accept, reject regex category by partial match works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject category regex t1\""]
)
# We should only see the second log message as we rejected the first
# via category rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_category_second_rule(self):
"""Test that fall-through reject, accept regex category on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept category regex non-existent\"",
"--filter \"accept category regex cat2\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the category of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,118 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
# System imports
from __future__ import print_function
import re
# LLDB imports
import lldb
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterRegexMessage(darwin_log.DarwinLogEventBasedTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
@decorators.skipUnlessDarwin
def test_filter_accept_message_full_match(self):
"""Test that fall-through reject, accept regex whole message works."""
log_entries = self.do_test(
["--no-match-accepts false",
# Note below, the four '\' characters are to get us two
# backslashes over on the gdb-remote side, which then
# becomes one as the cstr interprets it as an escape
# sequence. This needs to be rationalized. Initially I
# supported std::regex ECMAScript, which has the
# [[:digit:]] character classes and such. That was much
# more tenable. The backslashes have to travel through
# so many layers of escaping. (And note if you take
# off the Python raw string marker here, you need to put
# in 8 backslashes to go to two on the remote side.)
r'--filter "accept message regex log message sub2-cat\\\\d+"'])
# We should have received at least one log entry.
self.assertIsNotNone(log_entries,
"Log entry list should not be None.")
self.assertEqual(len(log_entries), 1,
"Should receive one log entry.")
self.assertRegexpMatches(log_entries[0]["message"], r"sub2-cat2",
"First os_log call should have been skipped.")
@decorators.skipUnlessDarwin
def test_filter_accept_message_partial_match(self):
"""Test that fall-through reject, accept regex message via partial
match works."""
log_entries = self.do_test(
["--no-match-accepts false",
"--filter \"accept message regex [^-]+2\""])
# We should only see the second log message as we only accept
# that message contents.
self.assertIsNotNone(log_entries,
"Log entry list should not be None.")
self.assertEqual(len(log_entries), 1,
"Should receive one log entry.")
self.assertRegexpMatches(log_entries[0]["message"], r"sub2-cat2",
"First os_log call should have been skipped.")
@decorators.skipUnlessDarwin
def test_filter_reject_message_full_match(self):
"""Test that fall-through accept, reject regex message works."""
log_entries = self.do_test(
["--no-match-accepts true",
"--filter \"reject message regex log message sub1-cat1\""])
# We should only see the second log message as we rejected the first
# via message contents rejection.
self.assertIsNotNone(log_entries,
"Log entry list should not be None.")
self.assertEqual(len(log_entries), 1,
"Should receive one log entry.")
self.assertRegexpMatches(log_entries[0]["message"], r"sub2-cat2",
"First os_log call should have been skipped.")
@decorators.skipUnlessDarwin
def test_filter_reject_message_partial_match(self):
"""Test that fall-through accept, reject regex message by partial
match works."""
log_entries = self.do_test(
["--no-match-accepts true",
"--filter \"reject message regex t1\""])
# We should only see the second log message as we rejected the first
# via partial message contents rejection.
self.assertIsNotNone(log_entries,
"Log entry list should not be None.")
self.assertEqual(len(log_entries), 1,
"Should receive one log entry.")
self.assertRegexpMatches(log_entries[0]["message"], r"sub2-cat2",
"First os_log call should have been skipped.")
@decorators.skipUnlessDarwin
def test_filter_accept_message_second_rule(self):
"""Test that fall-through reject, accept regex message on second rule
works."""
log_entries = self.do_test(
["--no-match-accepts false",
"--filter \"accept message regex non-existent\"",
"--filter \"accept message regex cat2\""])
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the message of the second log message.
self.assertIsNotNone(log_entries,
"Log entry list should not be None.")
self.assertEqual(len(log_entries), 1,
"Should receive one log entry.")
self.assertRegexpMatches(log_entries[0]["message"], r"sub2-cat2",
"First os_log call should have been skipped.")

View File

@ -0,0 +1,35 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_log(logger_sub1, "log message sub%d-cat%d", 1, 1);
os_log(logger_sub2, "log message sub%d-cat%d", 2, 2);
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,150 @@
"""
Test basic DarwinLog functionality provided by the StructuredDataDarwinLog
plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogFilterRegexSubsystem(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogFilterRegexSubsystem, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogFilterRegexSubsystem, self).tearDown()
# ==========================================================================
# basic filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_fallthrough_reject(self):
"""Test that a single fall-through reject regex rule rejects all logging."""
self.do_test(
["--no-match-accepts false"]
)
# We should not match any log lines.
self.assertIsNotNone(self.child.match)
self.assertFalse((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) in ["sub1", "sub2"]),
"log line should not have been received")
# ==========================================================================
# subsystem filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_filter_accept_subsystem_full_match(self):
"""Test that fall-through reject, accept regex single subsystem works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem regex org.llvm.lldb.test.sub2\""]
)
# We should only see the second log message as we only accept
# that subsystem.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_subsystem_partial_match(self):
"""Test that fall-through reject, accept regex subsystem via partial-match works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem regex org.llvm.+.sub2\""]
)
# We should only see the second log message as we only accept
# that subsystem.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_subsystem_full_match(self):
"""Test that fall-through accept, reject regex subsystem works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject subsystem regex org.llvm.lldb.test.sub1\""]
)
# We should only see the second log message as we rejected the first
# via subsystem rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_reject_subsystem_partial_match(self):
"""Test that fall-through accept, reject regex subsystem by partial match works."""
self.do_test(
["--no-match-accepts true",
"--filter \"reject subsystem regex org.*sub1\""]
)
# We should only see the second log message as we rejected the first
# via subsystem rejection.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_filter_accept_subsystem_second_rule(self):
"""Test that fall-through reject, accept regex subsystem on second rule works."""
self.do_test(
["--no-match-accepts false",
"--filter \"accept subsystem regex non-existent\"",
"--filter \"accept subsystem regex org.llvm.lldb.test.sub2\""
]
)
# We should only see the second message since we reject by default,
# the first filter doesn't match any, and the second filter matches
# the subsystem of the second log message.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) == "sub2"),
"first log line should not be present, second log line "
"should be")

View File

@ -0,0 +1,43 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_log(logger_sub1, "source-log-sub1-cat1");
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub2, "source-log-sub2-cat2");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,189 @@
"""
Test DarwinLog log message formatting options provided by the
StructuredDataDarwinLog plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogMessageFormat(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogMessageFormat, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogMessageFormat, self).tearDown()
# ==========================================================================
# Test settings around log message formatting
# ==========================================================================
REGEXES = [
re.compile(r"\[([^]]+)\] This is the log message."), # Match log
# with header.
re.compile(r"This is the log message."), # Match no-header content.
re.compile(r"exited with status") # Fallback if no log emitted.
]
@decorators.skipUnlessDarwin
def test_display_without_header_works(self):
"""Test that turning off log message headers works as advertised."""
self.do_test([], expect_regexes=self.REGEXES)
# We should not match the first pattern as we shouldn't have header
# content.
self.assertIsNotNone(self.child.match)
self.assertFalse((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should not have seen a header")
@decorators.skipUnlessDarwin
def test_display_with_header_works(self):
"""Test that displaying any header works."""
self.do_test(
["--timestamp-relative", "--subsystem", "--category",
"--activity-chain"],
expect_regexes=self.REGEXES,
settings_commands=[
"display-header true"
])
# We should match the first pattern as we should have header
# content.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should have printed a header")
def assert_header_contains_timestamp(self, header):
fields = header.split(',')
self.assertGreater(len(fields), 0,
"there should have been header content present")
self.assertRegexpMatches(fields[0],
r"^\d+:\d{2}:\d{2}.\d{9}$",
"time field should match expected format")
@decorators.skipUnlessDarwin
def test_header_timefield_only_works(self):
"""Test that displaying a header with only the timestamp works."""
self.do_test(["--timestamp-relative"], expect_regexes=self.REGEXES)
# We should match the first pattern as we should have header
# content.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should have printed a header")
header = self.child.match.group(1)
self.assertEqual(len(header.split(',')), 1,
"there should only be one header field")
self.assert_header_contains_timestamp(header)
@decorators.skipUnlessDarwin
def test_header_subsystem_only_works(self):
"""Test that displaying a header with only the subsystem works."""
self.do_test(["--subsystem"], expect_regexes=self.REGEXES)
# We should match the first pattern as we should have header
# content.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should have printed a header")
header = self.child.match.group(1)
self.assertEqual(len(header.split(',')), 1,
"there should only be one header field")
self.assertEquals(header,
"subsystem=org.llvm.lldb.test.sub1")
@decorators.skipUnlessDarwin
def test_header_category_only_works(self):
"""Test that displaying a header with only the category works."""
self.do_test(["--category"], expect_regexes=self.REGEXES)
# We should match the first pattern as we should have header
# content.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should have printed a header")
header = self.child.match.group(1)
self.assertEqual(len(header.split(',')), 1,
"there should only be one header field")
self.assertEquals(header,
"category=cat1")
@decorators.skipUnlessDarwin
def test_header_activity_chain_only_works(self):
"""Test that displaying a header with only the activity chain works."""
self.do_test(["--activity-chain"], expect_regexes=self.REGEXES)
# We should match the first pattern as we should have header
# content.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 0) and
(self.child.match.group(1) != ""),
"we should have printed a header")
header = self.child.match.group(1)
self.assertEqual(len(header.split(',')), 1,
"there should only be one header field")
self.assertEquals(header,
"activity-chain=parent-activity:child-activity")
# @decorators.skipUnlessDarwin
# def test_header_activity_no_chain_only_works(self):
# """Test that displaying a header with only the activity works."""
# self.do_test(
# [],
# expect_regexes=self.REGEXES,
# settings_commands=[
# "display-header true",
# "format-include-timestamp false",
# "format-include-activity true",
# "format-include-category false",
# "format-include-subsystem false",
# "display-activity-chain false"
# ])
# # We should match the first pattern as we should have header
# # content.
# self.assertIsNotNone(self.child.match)
# self.assertTrue((len(self.child.match.groups()) > 0) and
# (self.child.match.group(1) != ""),
# "we should have printed a header")
# header = self.child.match.group(1)
# self.assertEqual(len(header.split(',')), 1,
# "there should only be one header field")
# self.assertEquals(header,
# "activity=child-activity")

View File

@ -0,0 +1,41 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/activity.h>
#include <os/log.h>
#include <stdio.h>
#include "../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
if (!logger_sub1)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_activity_t parent_activity = os_activity_create("parent-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(parent_activity, ^{
os_activity_t child_activity = os_activity_create("child-activity",
OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_apply(child_activity, ^{
os_log(logger_sub1, "This is the log message.");
});
});
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,79 @@
"""
Test DarwinLog "source include debug-level" functionality provided by the
StructuredDataDarwinLog plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogSourceDebug(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogSourceDebug, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
# Indicate we want strict-sources behavior.
self.strict_sources = True
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogSourceDebug, self).tearDown()
# ==========================================================================
# source include/exclude debug filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
def test_source_default_exclude_debug(self):
"""Test that default excluding of debug-level log messages works."""
self.do_test([])
# We should only see the second log message as the first is a
# debug-level message and we're not including debug-level messages.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_source_explicitly_include_debug(self):
"""Test that explicitly including debug-level log messages works."""
self.do_test(["--debug"])
# We should only see the second log message as the first is a
# debug-level message and we're not including debug-level messages.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat1"),
"first log line should be present since we're "
"including debug-level log messages")

View File

@ -0,0 +1,34 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/log.h>
#include <stdio.h>
#include "../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_log_debug(logger_sub1, "source-log-sub1-cat1");
os_log(logger_sub2, "source-log-sub2-cat2");
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,82 @@
"""
Test DarwinLog "source include info-level" functionality provided by the
StructuredDataDarwinLog plugin.
These tests are currently only supported when running against Darwin
targets.
"""
from __future__ import print_function
import lldb
import os
import re
from lldbsuite.test import decorators
from lldbsuite.test import lldbtest
from lldbsuite.test import darwin_log
class TestDarwinLogSourceInfo(darwin_log.DarwinLogTestBase):
mydir = lldbtest.TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
super(TestDarwinLogSourceInfo, self).setUp()
# Source filename.
self.source = 'main.c'
# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}
# Locate breakpoint.
self.line = lldbtest.line_number(self.source, '// break here')
# Indicate we want strict-sources behavior.
self.strict_sources = True
def tearDown(self):
# Shut down the process if it's still running.
if self.child:
self.runCmd('process kill')
self.expect_prompt()
self.runCmd('quit')
# Let parent clean up
super(TestDarwinLogSourceInfo, self).tearDown()
# ==========================================================================
# source include/exclude debug filter tests
# ==========================================================================
@decorators.skipUnlessDarwin
@decorators.expectedFailureAll(bugnumber="rdar://27316264")
def test_source_exclude_info_level(self):
"""Test that default excluding of info-level log messages works."""
self.do_test([])
# We should only see the second log message as the first is an
# info-level message and we're not including debug-level messages.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat2"),
"first log line should not be present, second log line "
"should be")
@decorators.skipUnlessDarwin
def test_source_include_info_level(self):
"""Test that explicitly including info-level log messages works."""
self.do_test(
["--info"]
)
# We should only see the second log message as the first is a
# debug-level message and we're not including debug-level messages.
self.assertIsNotNone(self.child.match)
self.assertTrue((len(self.child.match.groups()) > 1) and
(self.child.match.group(2) == "cat1"),
"first log line should be present since we're "
"including info-level log messages")

View File

@ -0,0 +1,34 @@
//===-- main.c --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <os/log.h>
#include <stdio.h>
#include "../../common/darwin_log_common.h"
int main(int argc, char** argv)
{
os_log_t logger_sub1 = os_log_create("org.llvm.lldb.test.sub1", "cat1");
os_log_t logger_sub2 = os_log_create("org.llvm.lldb.test.sub2", "cat2");
if (!logger_sub1 || !logger_sub2)
return 1;
// Note we cannot use the os_log() line as the breakpoint because, as of
// the initial writing of this test, we get multiple breakpoints for that
// line, which confuses the pexpect test logic.
printf("About to log\n"); // break here
os_log_info(logger_sub1, "source-log-sub1-cat1");
os_log(logger_sub2, "source-log-sub2-cat2");
// Sleep, as the darwin log reporting doesn't always happen until a bit
// later. We need the message to come out before the process terminates.
sleep(FINAL_WAIT_SECONDS);
return 0;
}

View File

@ -44,7 +44,8 @@ public:
eBroadcastBitInterrupt = (1 << 1),
eBroadcastBitSTDOUT = (1 << 2),
eBroadcastBitSTDERR = (1 << 3),
eBroadcastBitProfileData = (1 << 4)
eBroadcastBitProfileData = (1 << 4),
eBroadcastBitStructuredData = (1 << 5)
};
SBProcess ();
@ -351,9 +352,15 @@ public:
static bool
GetInterruptedFromEvent (const lldb::SBEvent &event);
static lldb::SBStructuredData
GetStructuredDataFromEvent (const lldb::SBEvent &event);
static bool
EventIsProcessEvent (const lldb::SBEvent &event);
static bool
EventIsStructuredDataEvent (const lldb::SBEvent &event);
lldb::SBBroadcaster
GetBroadcaster () const;

View File

@ -0,0 +1,42 @@
//===-- SWIG Interface for SBStructuredData ---------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
namespace lldb {
%feature("docstring",
"A class representing a StructuredData event.
This class wraps the event type generated by StructuredData
features."
) SBStructuredData;
class SBStructuredData
{
public:
SBStructuredData();
SBStructuredData(const lldb::SBStructuredData &rhs);
SBStructuredData(const lldb::EventSP &event_sp);
~SBStructuredData();
bool
IsValid() const;
void
Clear();
lldb::SBError
GetAsJSON(lldb::SBStream &stream) const;
lldb::SBError
GetDescription(lldb::SBStream &stream) const;
};
}

View File

@ -178,7 +178,7 @@ public:
/// Some launch options specified by logical OR'ing
/// lldb::LaunchFlags enumeration values together.
///
/// @param[in] stop_at_endtry
/// @param[in] stop_at_entry
/// If false do not stop the inferior at the entry point.
///
/// @param[out]

View File

@ -95,6 +95,7 @@ import six
#include "lldb/API/SBSourceManager.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStringList.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBSymbol.h"
#include "lldb/API/SBSymbolContext.h"
#include "lldb/API/SBSymbolContextList.h"
@ -177,6 +178,7 @@ import six
%include "./interface/SBSourceManager.i"
%include "./interface/SBStream.i"
%include "./interface/SBStringList.i"
%include "./interface/SBStructuredData.i"
%include "./interface/SBSymbol.i"
%include "./interface/SBSymbolContext.i"
%include "./interface/SBSymbolContextList.i"

View File

@ -47,6 +47,7 @@ add_lldb_library(liblldb SHARED
SBSourceManager.cpp
SBStream.cpp
SBStringList.cpp
SBStructuredData.cpp
SBSymbol.cpp
SBSymbolContext.cpp
SBSymbolContextList.cpp

View File

@ -491,7 +491,7 @@ SBDebugger::HandleProcessEvent (const SBProcess &process, const SBEvent &event,
if (err != nullptr)
::fwrite (stdio_buffer, 1, len, err);
}
if (event_type & Process::eBroadcastBitStateChanged)
{
StateType event_state = SBProcess::GetStateFromEvent (event);

View File

@ -39,6 +39,7 @@
#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBMemoryRegionInfoList.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBThread.h"
#include "lldb/API/SBThreadCollection.h"
#include "lldb/API/SBStream.h"
@ -1029,8 +1030,16 @@ SBProcess::GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_
SBProcess
SBProcess::GetProcessFromEvent (const SBEvent &event)
{
SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get()));
return process;
ProcessSP process_sp =
Process::ProcessEventData::GetProcessFromEvent (event.get());
if (!process_sp)
{
// StructuredData events also know the process they come from.
// Try that.
process_sp = EventDataStructuredData::GetProcessFromEvent(event.get());
}
return SBProcess(process_sp);
}
bool
@ -1039,10 +1048,26 @@ SBProcess::GetInterruptedFromEvent (const SBEvent &event)
return Process::ProcessEventData::GetInterruptedFromEvent(event.get());
}
lldb::SBStructuredData
SBProcess::GetStructuredDataFromEvent (const lldb::SBEvent &event)
{
return SBStructuredData(event.GetSP());
}
bool
SBProcess::EventIsProcessEvent (const SBEvent &event)
{
return event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass();
return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) &&
!EventIsStructuredDataEvent (event);
}
bool
SBProcess::EventIsStructuredDataEvent (const lldb::SBEvent &event)
{
EventSP event_sp = event.GetSP();
EventData *event_data = event_sp ? event_sp->GetData() : nullptr;
return event_data &&
(event_data->GetFlavor() == EventDataStructuredData::GetFlavorString());
}
SBBroadcaster

View File

@ -0,0 +1,165 @@
//===-- SBStructuredData.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/API/SBStructuredData.h"
#include "lldb/API/SBStream.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Event.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StructuredData.h"
#include "lldb/Target/StructuredDataPlugin.h"
using namespace lldb;
using namespace lldb_private;
#pragma mark --
#pragma mark Impl
class SBStructuredData::Impl
{
public:
Impl() :
m_plugin_wp(),
m_data_sp()
{
}
Impl(const Impl &rhs) = default;
Impl(const EventSP &event_sp) :
m_plugin_wp(EventDataStructuredData::GetPluginFromEvent(event_sp.get())),
m_data_sp(EventDataStructuredData::GetObjectFromEvent(event_sp.get()))
{
}
~Impl() = default;
Impl&
operator =(const Impl &rhs) = default;
bool
IsValid() const
{
return m_data_sp.get() != nullptr;
}
void
Clear()
{
m_plugin_wp.reset();
m_data_sp.reset();
}
SBError
GetAsJSON(lldb::SBStream &stream) const
{
SBError sb_error;
if (!m_data_sp)
{
sb_error.SetErrorString("No structured data.");
return sb_error;
}
m_data_sp->Dump(stream.ref());
return sb_error;
}
lldb::SBError
GetDescription(lldb::SBStream &stream) const
{
SBError sb_error;
if (!m_data_sp)
{
sb_error.SetErrorString("Cannot pretty print structured data: "
"no data to print.");
return sb_error;
}
// Grab the plugin.
auto plugin_sp = StructuredDataPluginSP(m_plugin_wp);
if (!plugin_sp)
{
sb_error.SetErrorString("Cannot pretty print structured data: "
"plugin doesn't exist.");
return sb_error;
}
// Get the data's description.
auto error = plugin_sp->GetDescription(m_data_sp, stream.ref());
if (!error.Success())
sb_error.SetError(error);
return sb_error;
}
private:
StructuredDataPluginWP m_plugin_wp;
StructuredData::ObjectSP m_data_sp;
};
#pragma mark --
#pragma mark SBStructuredData
SBStructuredData::SBStructuredData() :
m_impl_up(new Impl())
{
}
SBStructuredData::SBStructuredData(const lldb::SBStructuredData &rhs) :
m_impl_up(new Impl(*rhs.m_impl_up.get()))
{
}
SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp) :
m_impl_up(new Impl(event_sp))
{
}
SBStructuredData::~SBStructuredData()
{
}
SBStructuredData &
SBStructuredData::operator =(const lldb::SBStructuredData &rhs)
{
*m_impl_up = *rhs.m_impl_up;
return *this;
}
bool
SBStructuredData::IsValid() const
{
return m_impl_up->IsValid();
}
void
SBStructuredData::Clear()
{
m_impl_up->Clear();
}
SBError
SBStructuredData::GetAsJSON(lldb::SBStream &stream) const
{
return m_impl_up->GetAsJSON(stream);
}
lldb::SBError
SBStructuredData::GetDescription(lldb::SBStream &stream) const
{
return m_impl_up->GetDescription(stream);
}

View File

@ -100,6 +100,7 @@
#include "Plugins/Process/mach-core/ProcessMachCore.h"
#include "Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h"
#endif
#include "Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h"
#if defined(__FreeBSD__)
#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h"
@ -381,6 +382,11 @@ SystemInitializerFull::Initialize()
PlatformRemoteAppleWatch::Initialize();
DynamicLoaderDarwinKernel::Initialize();
#endif
// This plugin is valid on any host that talks to a Darwin remote.
// It shouldn't be limited to __APPLE__.
StructuredDataDarwinLog::Initialize();
//----------------------------------------------------------------------
// Platform agnostic plugins
//----------------------------------------------------------------------
@ -513,6 +519,8 @@ SystemInitializerFull::Terminate()
platform_gdb_server::PlatformRemoteGDBServer::Terminate();
process_gdb_remote::ProcessGDBRemote::Terminate();
StructuredDataDarwinLog::Terminate();
DynamicLoaderMacOSXDYLD::Terminate();
DynamicLoaderMacOS::Terminate();
DynamicLoaderPOSIXDYLD::Terminate();

View File

@ -56,6 +56,7 @@
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/StructuredDataPlugin.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/AnsiTerminal.h"
@ -1476,14 +1477,15 @@ Debugger::GetProcessSTDERR (Process *process, Stream *stream)
return total_bytes;
}
// This function handles events that were broadcast by the process.
void
Debugger::HandleProcessEvent (const EventSP &event_sp)
{
using namespace lldb;
const uint32_t event_type = event_sp->GetType();
ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
ProcessSP process_sp = (event_type == Process::eBroadcastBitStructuredData)
? EventDataStructuredData::GetProcessFromEvent(event_sp.get())
: Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
StreamSP output_stream_sp = GetAsyncOutputStream();
StreamSP error_stream_sp = GetAsyncErrorStream();
@ -1498,6 +1500,9 @@ Debugger::HandleProcessEvent (const EventSP &event_sp)
const bool got_state_changed = (event_type & Process::eBroadcastBitStateChanged) != 0;
const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
const bool got_structured_data = (event_type &
Process::eBroadcastBitStructuredData) != 0;
if (got_state_changed)
{
StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get());
@ -1522,6 +1527,45 @@ Debugger::HandleProcessEvent (const EventSP &event_sp)
GetProcessSTDERR (process_sp.get(), error_stream_sp.get());
}
// Give structured data events an opportunity to display.
if (got_structured_data)
{
StructuredDataPluginSP plugin_sp =
EventDataStructuredData::GetPluginFromEvent(event_sp.get());
if (plugin_sp)
{
auto structured_data_sp =
EventDataStructuredData::GetObjectFromEvent(event_sp.get());
if (output_stream_sp)
{
StreamString content_stream;
Error error = plugin_sp->GetDescription(structured_data_sp,
content_stream);
if (error.Success())
{
if (!content_stream.GetString().empty())
{
// Add newline.
content_stream.PutChar('\n');
content_stream.Flush();
// Print it.
output_stream_sp->PutCString(content_stream
.GetString().c_str());
}
}
else
{
error_stream_sp->Printf("Failed to print structured "
"data with plugin %s: %s",
plugin_sp->GetPluginName()
.AsCString(),
error.AsCString());
}
}
}
}
// Now display any stopped state changes after any STDIO
if (got_state_changed && state_is_stopped)
{
@ -1586,7 +1630,8 @@ Debugger::DefaultEventHandler()
BroadcastEventSpec process_event_spec (broadcaster_class_process,
Process::eBroadcastBitStateChanged |
Process::eBroadcastBitSTDOUT |
Process::eBroadcastBitSTDERR);
Process::eBroadcastBitSTDERR |
Process::eBroadcastBitStructuredData);
BroadcastEventSpec thread_event_spec (broadcaster_class_thread,
Thread::eBroadcastBitStackChanged |

View File

@ -25,6 +25,13 @@
using namespace lldb;
using namespace lldb_private;
#pragma mark -
#pragma mark Event
//------------------------------------------------------------------
// Event functions
//------------------------------------------------------------------
Event::Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data) :
m_broadcaster_wp(broadcaster->GetBroadcasterImpl()),
m_type(event_type),
@ -101,6 +108,13 @@ Event::DoOnRemoval ()
m_data_sp->DoOnRemoval (this);
}
#pragma mark -
#pragma mark EventData
//------------------------------------------------------------------
// EventData functions
//------------------------------------------------------------------
EventData::EventData() = default;
EventData::~EventData() = default;
@ -111,6 +125,14 @@ EventData::Dump (Stream *s) const
s->PutCString ("Generic Event Data");
}
#pragma mark -
#pragma mark EventDataBytes
//------------------------------------------------------------------
// EventDataBytes functions
//------------------------------------------------------------------
EventDataBytes::EventDataBytes () :
m_bytes()
{
@ -224,3 +246,150 @@ EventDataBytes::SwapBytes (std::string &new_bytes)
{
m_bytes.swap (new_bytes);
}
#pragma mark -
#pragma mark EventStructuredData
//------------------------------------------------------------------
// EventDataStructuredData definitions
//------------------------------------------------------------------
EventDataStructuredData::EventDataStructuredData() :
EventData(),
m_process_sp(),
m_object_sp(),
m_plugin_sp()
{
}
EventDataStructuredData::EventDataStructuredData(const ProcessSP &process_sp,
const StructuredData::ObjectSP
&object_sp,
const
lldb::StructuredDataPluginSP
&plugin_sp) :
EventData(),
m_process_sp(process_sp),
m_object_sp(object_sp),
m_plugin_sp(plugin_sp)
{
}
EventDataStructuredData::~EventDataStructuredData()
{
}
//------------------------------------------------------------------
// EventDataStructuredData member functions
//------------------------------------------------------------------
const ConstString &
EventDataStructuredData::GetFlavor() const
{
return EventDataStructuredData::GetFlavorString();
}
void
EventDataStructuredData::Dump(Stream *s) const
{
if (!s)
return;
if (m_object_sp)
m_object_sp->Dump(*s);
}
const ProcessSP&
EventDataStructuredData::GetProcess() const
{
return m_process_sp;
}
const StructuredData::ObjectSP&
EventDataStructuredData::GetObject() const
{
return m_object_sp;
}
const lldb::StructuredDataPluginSP&
EventDataStructuredData::GetStructuredDataPlugin() const
{
return m_plugin_sp;
}
void
EventDataStructuredData::SetProcess(const ProcessSP &process_sp)
{
m_process_sp = process_sp;
}
void
EventDataStructuredData::SetObject(const StructuredData::ObjectSP &object_sp)
{
m_object_sp = object_sp;
}
void
EventDataStructuredData::SetStructuredDataPlugin(const
lldb::StructuredDataPluginSP
&plugin_sp)
{
m_plugin_sp = plugin_sp;
}
//------------------------------------------------------------------
// EventDataStructuredData static functions
//------------------------------------------------------------------
const EventDataStructuredData*
EventDataStructuredData::GetEventDataFromEvent(const Event *event_ptr)
{
if (event_ptr == nullptr)
return nullptr;
const EventData *event_data = event_ptr->GetData();
if (!event_data || event_data->GetFlavor() !=
EventDataStructuredData::GetFlavorString())
return nullptr;
return static_cast<const EventDataStructuredData*>(event_data);
}
ProcessSP
EventDataStructuredData::GetProcessFromEvent(const Event *event_ptr)
{
auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
if (event_data)
return event_data->GetProcess();
else
return ProcessSP();
}
StructuredData::ObjectSP
EventDataStructuredData::GetObjectFromEvent(const Event *event_ptr)
{
auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
if (event_data)
return event_data->GetObject();
else
return StructuredData::ObjectSP();
}
lldb::StructuredDataPluginSP
EventDataStructuredData::GetPluginFromEvent(const Event *event_ptr)
{
auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
if (event_data)
return event_data->GetStructuredDataPlugin();
else
return StructuredDataPluginSP();
}
const ConstString &
EventDataStructuredData::GetFlavorString ()
{
static ConstString s_flavor("EventDataStructuredData");
return s_flavor;
}

View File

@ -1917,6 +1917,146 @@ PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
return none_instance(interpreter);
}
#pragma mark -
#pragma mark StructuredDataPlugin
// -----------------------------------------------------------------------------
// StructuredDataPlugin
// -----------------------------------------------------------------------------
struct StructuredDataPluginInstance
{
StructuredDataPluginInstance() :
name(),
description(),
create_callback(nullptr),
debugger_init_callback(nullptr),
filter_callback(nullptr)
{
}
ConstString name;
std::string description;
StructuredDataPluginCreateInstance create_callback;
DebuggerInitializeCallback debugger_init_callback;
StructuredDataFilterLaunchInfo filter_callback;
};
typedef std::vector<StructuredDataPluginInstance> StructuredDataPluginInstances;
static std::recursive_mutex &
GetStructuredDataPluginMutex()
{
static std::recursive_mutex g_instances_mutex;
return g_instances_mutex;
}
static StructuredDataPluginInstances &
GetStructuredDataPluginInstances ()
{
static StructuredDataPluginInstances g_instances;
return g_instances;
}
bool
PluginManager::RegisterPlugin(const ConstString &name,
const char *description,
StructuredDataPluginCreateInstance
create_callback,
DebuggerInitializeCallback debugger_init_callback,
StructuredDataFilterLaunchInfo filter_callback)
{
if (create_callback)
{
StructuredDataPluginInstance instance;
assert((bool)name);
instance.name = name;
if (description && description[0])
instance.description = description;
instance.create_callback = create_callback;
instance.debugger_init_callback = debugger_init_callback;
instance.filter_callback = filter_callback;
std::lock_guard<std::recursive_mutex> guard(
GetStructuredDataPluginMutex());
GetStructuredDataPluginInstances().push_back(instance);
}
return false;
}
bool
PluginManager::UnregisterPlugin(StructuredDataPluginCreateInstance create_callback)
{
if (create_callback)
{
std::lock_guard<std::recursive_mutex> guard(
GetStructuredDataPluginMutex());
StructuredDataPluginInstances &instances =
GetStructuredDataPluginInstances();
StructuredDataPluginInstances::iterator pos, end = instances.end();
for (pos = instances.begin(); pos != end; ++ pos)
{
if (pos->create_callback == create_callback)
{
instances.erase(pos);
return true;
}
}
}
return false;
}
StructuredDataPluginCreateInstance
PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(uint32_t idx)
{
std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
StructuredDataPluginInstances &instances =
GetStructuredDataPluginInstances();
if (idx < instances.size())
return instances[idx].create_callback;
return nullptr;
}
StructuredDataPluginCreateInstance
PluginManager::GetStructuredDataPluginCreateCallbackForPluginName(
const ConstString &name)
{
if (name)
{
std::lock_guard<std::recursive_mutex> guard(
GetStructuredDataPluginMutex());
StructuredDataPluginInstances &instances =
GetStructuredDataPluginInstances();
StructuredDataPluginInstances::iterator pos, end = instances.end();
for (pos = instances.begin(); pos != end; ++ pos)
{
if (name == pos->name)
return pos->create_callback;
}
}
return nullptr;
}
StructuredDataFilterLaunchInfo
PluginManager::GetStructuredDataFilterCallbackAtIndex(uint32_t idx,
bool &iteration_complete)
{
std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
StructuredDataPluginInstances &instances =
GetStructuredDataPluginInstances();
if (idx < instances.size())
{
iteration_complete = false;
return instances[idx].filter_callback;
}
else
{
iteration_complete = true;
}
return nullptr;
}
#pragma mark SymbolFile
struct SymbolFileInstance
@ -2772,6 +2912,17 @@ PluginManager::DebuggerInitialize (Debugger &debugger)
os.debugger_init_callback(debugger);
}
}
// Initialize the StructuredDataPlugin plugins
{
std::lock_guard<std::recursive_mutex>
guard(GetStructuredDataPluginMutex());
for (auto &plugin: GetStructuredDataPluginInstances())
{
if (plugin.debugger_init_callback)
plugin.debugger_init_callback(debugger);
}
}
}
// This is the preferred new way to register plugin specific settings. e.g.
@ -2905,6 +3056,7 @@ const char* kPlatformPluginName("platform");
const char* kProcessPluginName("process");
const char* kSymbolFilePluginName("symbol-file");
const char* kJITLoaderPluginName("jit-loader");
const char* kStructuredDataPluginName("structured-data");
} // anonymous namespace
@ -3049,3 +3201,24 @@ PluginManager::CreateSettingForOperatingSystemPlugin(Debugger &debugger,
}
return false;
}
lldb::OptionValuePropertiesSP
PluginManager::GetSettingForStructuredDataPlugin(Debugger &debugger,
const ConstString &setting_name)
{
return GetSettingForPlugin(debugger, setting_name, ConstString(kStructuredDataPluginName));
}
bool
PluginManager::CreateSettingForStructuredDataPlugin(Debugger &debugger,
const lldb::OptionValuePropertiesSP &properties_sp,
const ConstString &description,
bool is_global_property)
{
return CreateSettingForPlugin(debugger,
ConstString(kStructuredDataPluginName),
ConstString("Settings for structured data plug-ins"),
properties_sp,
description,
is_global_property);
}

View File

@ -1170,8 +1170,48 @@ Args::LongestCommonPrefix (std::string &common_prefix)
}
}
void
Args::AddOrReplaceEnvironmentVariable(const char *env_var_name,
const char *new_value)
{
if (!env_var_name || !new_value)
return;
// Build the new entry.
StreamString stream;
stream << env_var_name;
stream << '=';
stream << new_value;
stream.Flush();
// Find the environment variable if present and replace it.
for (size_t i = 0; i < GetArgumentCount(); ++i)
{
// Get the env var value.
const char *arg_value = GetArgumentAtIndex(i);
if (!arg_value)
continue;
// Find the name of the env var: before the first =.
auto equal_p = strchr(arg_value, '=');
if (!equal_p)
continue;
// Check if the name matches the given env_var_name.
if (strncmp(env_var_name, arg_value, equal_p - arg_value) == 0)
{
ReplaceArgumentAtIndex(i, stream.GetString().c_str());
return;
}
}
// We didn't find it. Append it instead.
AppendArgument(stream.GetString().c_str());
}
bool
Args::ContainsEnvironmentVariable(const char *env_var_name) const
Args::ContainsEnvironmentVariable(const char *env_var_name,
size_t *argument_index) const
{
// Validate args.
if (!env_var_name)
@ -1193,6 +1233,8 @@ Args::ContainsEnvironmentVariable(const char *env_var_name) const
equal_p - argument_value) == 0)
{
// We matched.
if (argument_index)
*argument_index = i;
return true;
}
}
@ -1202,6 +1244,8 @@ Args::ContainsEnvironmentVariable(const char *env_var_name) const
if (strcmp(argument_value, env_var_name) == 0)
{
// We matched.
if (argument_index)
*argument_index = i;
return true;
}
}

View File

@ -11,6 +11,7 @@
#include "llvm/ADT/StringExtras.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/LLDBAssert.h"
@ -100,6 +101,41 @@ GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(ContinueDelegate &dele
case 'A':
delegate.HandleAsyncMisc(llvm::StringRef(response.GetStringRef()).substr(1));
break;
case 'J':
// Asynchronous JSON packet, destined for a
// StructuredDataPlugin.
{
// Parse the content into a StructuredData instance.
auto payload_index = strlen("JSON-async:");
StructuredData::ObjectSP json_sp =
StructuredData::ParseJSON(response.GetStringRef()
.substr(payload_index));
if (log)
{
if (json_sp)
log->Printf(
"GDBRemoteCommmunicationClientBase::%s() "
"received Async StructuredData packet: %s",
__FUNCTION__,
response.GetStringRef().
substr(payload_index).c_str());
else
log->Printf("GDBRemoteCommmunicationClientBase::%s"
"() received StructuredData packet:"
" parse failure", __FUNCTION__);
}
// Pass the data to the process to route to the
// appropriate plugin. The plugin controls what happens
// to it from there.
bool routed = delegate.HandleAsyncStructuredData(json_sp);
if (log)
log->Printf("GDBRemoteCommmunicationClientBase::%s()"
" packet %s", __FUNCTION__,
routed ? "handled" : "not handled");
break;
}
case 'T':
case 'S':
// Do this with the continue lock held.

View File

@ -31,6 +31,16 @@ public:
HandleAsyncMisc(llvm::StringRef data) = 0;
virtual void
HandleStopReply() = 0;
//
/// Processes async structured data.
///
/// @return
/// true if the data was handled; otherwise, false.
//
virtual bool
HandleAsyncStructuredData(const StructuredData::ObjectSP
&object_sp) = 0;
};
GDBRemoteClientBase(const char *comm_name, const char *listener_name);

View File

@ -30,6 +30,8 @@
#include "lldb/Interpreter/Args.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Target/Target.h"
// Project includes
@ -114,7 +116,10 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
m_gdb_server_name(),
m_gdb_server_version(UINT32_MAX),
m_default_packet_timeout(0),
m_max_packet_size(0)
m_max_packet_size(0),
m_qSupported_response(),
m_supported_async_json_packets_is_valid(false),
m_supported_async_json_packets_sp()
{
}
@ -378,6 +383,9 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings (bool did_exec)
m_gdb_server_version = UINT32_MAX;
m_default_packet_timeout = 0;
m_max_packet_size = 0;
m_qSupported_response.clear();
m_supported_async_json_packets_is_valid = false;
m_supported_async_json_packets_sp.reset();
}
// These flags should be reset when we first connect to a GDB server
@ -413,6 +421,12 @@ GDBRemoteCommunicationClient::GetRemoteQSupported ()
/*send_async=*/false) == PacketResult::Success)
{
const char *response_cstr = response.GetStringRef().c_str();
// Hang on to the qSupported packet, so that platforms can do custom
// configuration of the transport before attaching/launching the
// process.
m_qSupported_response = response_cstr;
if (::strstr (response_cstr, "qXfer:auxv:read+"))
m_supports_qXfer_auxv_read = eLazyBoolYes;
if (::strstr (response_cstr, "qXfer:libraries-svr4:read+"))
@ -3906,6 +3920,126 @@ GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
}
}
StructuredData::Array*
GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins()
{
if (!m_supported_async_json_packets_is_valid)
{
// Query the server for the array of supported asynchronous JSON
// packets.
m_supported_async_json_packets_is_valid = true;
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(
GDBR_LOG_PROCESS));
// Poll it now.
StringExtractorGDBRemote response;
const bool send_async = false;
if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response,
send_async) == PacketResult::Success)
{
m_supported_async_json_packets_sp = StructuredData::ParseJSON(
response.GetStringRef());
if (m_supported_async_json_packets_sp &&
!m_supported_async_json_packets_sp->GetAsArray())
{
// We were returned something other than a JSON array. This
// is invalid. Clear it out.
if (log)
log->Printf("GDBRemoteCommunicationClient::%s(): "
"QSupportedAsyncJSONPackets returned invalid "
"result: %s", __FUNCTION__,
response.GetStringRef().c_str());
m_supported_async_json_packets_sp.reset();
}
}
else
{
if (log)
log->Printf("GDBRemoteCommunicationClient::%s(): "
"QSupportedAsyncJSONPackets unsupported",
__FUNCTION__);
}
if (log && m_supported_async_json_packets_is_valid)
{
StreamString stream;
m_supported_async_json_packets_sp->Dump(stream);
log->Printf("GDBRemoteCommunicationClient::%s(): supported async "
"JSON packets: %s", __FUNCTION__,
stream.GetString().c_str());
}
}
return m_supported_async_json_packets_sp
? m_supported_async_json_packets_sp->GetAsArray()
: nullptr;
}
Error
GDBRemoteCommunicationClient::ConfigureRemoteStructuredData(
const ConstString &type_name,
const StructuredData::ObjectSP &config_sp)
{
Error error;
if (type_name.GetLength() == 0)
{
error.SetErrorString("invalid type_name argument");
return error;
}
// Build command: Configure{type_name}: serialized config
// data.
StreamGDBRemote stream;
stream.PutCString("QConfigure");
stream.PutCString(type_name.AsCString());
stream.PutChar(':');
if (config_sp)
{
// Gather the plain-text version of the configuration data.
StreamString unescaped_stream;
config_sp->Dump(unescaped_stream);
unescaped_stream.Flush();
// Add it to the stream in escaped fashion.
stream.PutEscapedBytes(unescaped_stream.GetData(),
unescaped_stream.GetSize());
}
stream.Flush();
// Send the packet.
const bool send_async = false;
StringExtractorGDBRemote response;
auto result = SendPacketAndWaitForResponse(stream.GetString().c_str(),
response, send_async);
if (result == PacketResult::Success)
{
// We failed if the config result comes back other than OK.
if (strcmp(response.GetStringRef().c_str(), "OK") == 0)
{
// Okay!
error.Clear();
}
else
{
error.SetErrorStringWithFormat("configuring StructuredData feature "
"%s failed with error %s",
type_name.AsCString(),
response.GetStringRef().c_str());
}
}
else
{
// Can we get more data here on the failure?
error.SetErrorStringWithFormat("configuring StructuredData feature %s "
"failed when sending packet: "
"PacketResult=%d", type_name.AsCString(),
result);
}
return error;
}
void
GDBRemoteCommunicationClient::OnRunPacketSent(bool first)
{

View File

@ -547,6 +547,53 @@ public:
void
ServeSymbolLookups(lldb_private::Process *process);
//------------------------------------------------------------------
/// Return the feature set supported by the gdb-remote server.
///
/// This method returns the remote side's response to the qSupported
/// packet. The response is the complete string payload returned
/// to the client.
///
/// @return
/// The string returned by the server to the qSupported query.
//------------------------------------------------------------------
const std::string&
GetServerSupportedFeatures() const
{
return m_qSupported_response;
}
//------------------------------------------------------------------
/// Return the array of async JSON packet types supported by the remote.
///
/// This method returns the remote side's array of supported JSON
/// packet types as a list of type names. Each of the results are
/// expected to have an Enable{type_name} command to enable and configure
/// the related feature. Each type_name for an enabled feature will
/// possibly send async-style packets that contain a payload of a
/// binhex-encoded JSON dictionary. The dictionary will have a
/// string field named 'type', that contains the type_name of the
/// supported packet type.
///
/// There is a Plugin category called structured-data plugins.
/// A plugin indicates whether it knows how to handle a type_name.
/// If so, it can be used to process the async JSON packet.
///
/// @return
/// The string returned by the server to the qSupported query.
//------------------------------------------------------------------
lldb_private::StructuredData::Array*
GetSupportedStructuredDataPlugins();
//------------------------------------------------------------------
/// Configure a StructuredData feature on the remote end.
///
/// @see \b Process::ConfigureStructuredData(...) for details.
//------------------------------------------------------------------
Error
ConfigureRemoteStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp);
protected:
LazyBool m_supports_not_sending_acks;
LazyBool m_supports_thread_suffix;
@ -617,6 +664,10 @@ protected:
uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if qGDBServerVersion is not supported
uint32_t m_default_packet_timeout;
uint64_t m_max_packet_size; // as returned by qSupported
std::string m_qSupported_response; // the complete response to qSupported
bool m_supported_async_json_packets_is_valid;
lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
bool
GetCurrentProcessInfo (bool allow_lazy_pid = true);

View File

@ -1175,7 +1175,7 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch)
{
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
if (log)
log->Printf ("ProcessGDBRemote::DidLaunch()");
log->Printf ("ProcessGDBRemote::%s()", __FUNCTION__);
if (GetID() != LLDB_INVALID_PROCESS_ID)
{
BuildDynamicRegisterInfo (false);
@ -1271,6 +1271,13 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch)
GetTarget().SetArchitecture (process_arch);
}
}
// Find out which StructuredDataPlugins are supported by the
// debug monitor. These plugins transmit data over async $J packets.
auto supported_packets_array =
m_gdb_comm.GetSupportedStructuredDataPlugins();
if (supported_packets_array)
MapSupportedStructuredDataPlugins(*supported_packets_array);
}
}
@ -4342,6 +4349,13 @@ ProcessGDBRemote::GetSharedCacheInfo ()
return object_sp;
}
Error
ProcessGDBRemote::ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP
&config_sp)
{
return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp);
}
// Establish the largest memory read/write payloads we should use.
// If the remote stub has a max packet size, stay under that size.
@ -5235,6 +5249,13 @@ ProcessGDBRemote::HandleStopReply()
BuildDynamicRegisterInfo(true);
}
bool
ProcessGDBRemote::HandleAsyncStructuredData(const StructuredData::ObjectSP
&object_sp)
{
return RouteAsyncStructuredData(object_sp);
}
class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed
{
public:

View File

@ -261,6 +261,10 @@ public:
StructuredData::ObjectSP
GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) override;
Error
ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp) override;
StructuredData::ObjectSP
GetLoadedDynamicLibrariesInfos () override;
@ -506,6 +510,9 @@ private:
HandleAsyncMisc(llvm::StringRef data) override;
void
HandleStopReply() override;
bool
HandleAsyncStructuredData(const StructuredData::ObjectSP
&object_sp) override;
DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
};

View File

@ -0,0 +1,2 @@
add_subdirectory(DarwinLog)

View File

@ -0,0 +1,5 @@
list(APPEND SOURCES
StructuredDataDarwinLog.cpp
)
add_lldb_library(lldbPluginStructuredDataDarwinLog ${SOURCES})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,164 @@
//===-- StructuredDataDarwinLog.h -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef StructuredDataDarwinLog_h
#define StructuredDataDarwinLog_h
#include "lldb/Target/StructuredDataPlugin.h"
#include <mutex>
// Forward declarations
namespace sddarwinlog_private
{
class EnableCommand;
}
namespace lldb_private
{
class StructuredDataDarwinLog : public StructuredDataPlugin
{
friend sddarwinlog_private::EnableCommand;
public:
// -------------------------------------------------------------------------
// Public static API
// -------------------------------------------------------------------------
static void
Initialize();
static void
Terminate();
static const ConstString&
GetStaticPluginName();
// -------------------------------------------------------------------------
/// Return whether the DarwinLog functionality is enabled.
///
/// The DarwinLog functionality is enabled if the user expicitly enabled
/// it with the enable command, or if the user has the setting set
/// that controls if we always enable it for newly created/attached
/// processes.
///
/// @return
/// True if DarwinLog support is/will be enabled for existing or
/// newly launched/attached processes.
// -------------------------------------------------------------------------
static bool
IsEnabled();
// -------------------------------------------------------------------------
// PluginInterface API
// -------------------------------------------------------------------------
ConstString
GetPluginName() override;
uint32_t
GetPluginVersion() override;
// -------------------------------------------------------------------------
// StructuredDataPlugin API
// -------------------------------------------------------------------------
bool
SupportsStructuredDataType(const ConstString &type_name) override;
void
HandleArrivalOfStructuredData(Process &process,
const ConstString &type_name,
const StructuredData::ObjectSP
&object_sp) override;
Error
GetDescription(const StructuredData::ObjectSP &object_sp,
lldb_private::Stream &stream) override;
bool
GetEnabled(const ConstString &type_name) const override;
void
ModulesDidLoad(Process &process, ModuleList &module_list) override;
private:
// -------------------------------------------------------------------------
// Private constructors
// -------------------------------------------------------------------------
StructuredDataDarwinLog(const lldb::ProcessWP &process_wp);
// -------------------------------------------------------------------------
// Private static methods
// -------------------------------------------------------------------------
static lldb::StructuredDataPluginSP
CreateInstance(Process &process);
static void
DebuggerInitialize(Debugger &debugger);
static bool
InitCompletionHookCallback(void *baton, StoppointCallbackContext *context,
lldb::user_id_t break_id,
lldb::user_id_t break_loc_id);
static Error
FilterLaunchInfo(ProcessLaunchInfo &launch_info, Target *target);
// -------------------------------------------------------------------------
// Internal helper methods used by friend classes
// -------------------------------------------------------------------------
void
SetEnabled(bool enabled);
void
AddInitCompletionHook(Process &process);
// -------------------------------------------------------------------------
// Private methods
// -------------------------------------------------------------------------
void
DumpTimestamp(Stream &stream, uint64_t timestamp);
size_t
DumpHeader(Stream &stream, const StructuredData::Dictionary &event);
size_t
HandleDisplayOfEvent(const StructuredData::Dictionary &event,
Stream &stream);
// -------------------------------------------------------------------------
/// Call the enable command again, using whatever settings were initially
/// made.
// -------------------------------------------------------------------------
void
EnableNow();
// -------------------------------------------------------------------------
// Private data
// -------------------------------------------------------------------------
bool m_recorded_first_timestamp;
uint64_t m_first_timestamp_seen;
bool m_is_enabled;
std::mutex m_added_breakpoint_mutex;
bool m_added_breakpoint;
};
}
#endif /* StructuredDataPluginDarwinLog_hpp */

View File

@ -30,6 +30,7 @@ add_lldb_library(lldbTarget
StackFrameList.cpp
StackID.cpp
StopInfo.cpp
StructuredDataPlugin.cpp
SystemRuntime.cpp
Target.cpp
TargetList.cpp
@ -40,6 +41,7 @@ add_lldb_library(lldbTarget
ThreadPlanBase.cpp
ThreadPlanCallFunction.cpp
ThreadPlanCallFunctionUsingABI.cpp
ThreadPlanCallOnFunctionExit.cpp
ThreadPlanCallUserExpression.cpp
ThreadPlanPython.cpp
ThreadPlanRunToAddress.cpp

View File

@ -1314,7 +1314,34 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info,
// group, since then we can handle ^C interrupts ourselves w/o having to worry
// about the target getting them as well.
launch_info.SetLaunchInSeparateProcessGroup(true);
// Allow any StructuredData process-bound plugins to adjust the launch info
// if needed
size_t i = 0;
bool iteration_complete = false;
// Note iteration can't simply go until a nullptr callback is returned, as
// it is valid for a plugin to not supply a filter.
auto get_filter_func =
PluginManager::GetStructuredDataFilterCallbackAtIndex;
for (auto filter_callback = get_filter_func(i, iteration_complete);
!iteration_complete;
filter_callback = get_filter_func(++i, iteration_complete))
{
if (filter_callback)
{
// Give this ProcessLaunchInfo filter a chance to adjust the launch
// info.
error = (*filter_callback)(launch_info, target);
if (!error.Success())
{
if (log)
log->Printf("Platform::%s() StructuredDataPlugin launch "
"filter failed.", __FUNCTION__);
return process_sp;
}
}
}
error = LaunchProcess (launch_info);
if (error.Success())
{

View File

@ -54,6 +54,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/StructuredDataPlugin.h"
#include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/TargetList.h"
@ -797,6 +798,7 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, const UnixSig
SetEventName(eBroadcastBitSTDOUT, "stdout-available");
SetEventName(eBroadcastBitSTDERR, "stderr-available");
SetEventName(eBroadcastBitProfileData, "profile-data-available");
SetEventName(eBroadcastBitStructuredData, "structured-data-available");
m_private_state_control_broadcaster.SetEventName(eBroadcastInternalStateControlStop, "control-stop");
m_private_state_control_broadcaster.SetEventName(eBroadcastInternalStateControlPause, "control-pause");
@ -804,7 +806,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, const UnixSig
m_listener_sp->StartListeningForEvents(this, eBroadcastBitStateChanged | eBroadcastBitInterrupt |
eBroadcastBitSTDOUT | eBroadcastBitSTDERR |
eBroadcastBitProfileData);
eBroadcastBitProfileData |
eBroadcastBitStructuredData);
m_private_state_listener_sp->StartListeningForEvents(&m_private_state_broadcaster,
eBroadcastBitStateChanged | eBroadcastBitInterrupt);
@ -4795,6 +4798,25 @@ Process::BroadcastAsyncProfileData(const std::string &one_profile_data)
BroadcastEventIfUnique (eBroadcastBitProfileData, new ProcessEventData (shared_from_this(), GetState()));
}
void
Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
const StructuredDataPluginSP &plugin_sp)
{
BroadcastEvent(eBroadcastBitStructuredData,
new EventDataStructuredData(shared_from_this(),
object_sp, plugin_sp));
}
StructuredDataPluginSP
Process::GetStructuredDataPlugin(const ConstString &type_name) const
{
auto find_it = m_structured_data_plugin_map.find(type_name);
if (find_it != m_structured_data_plugin_map.end())
return find_it->second;
else
return StructuredDataPluginSP();
}
size_t
Process::GetAsyncProfileData (char *buf, size_t buf_size, Error &error)
{
@ -6381,6 +6403,13 @@ Process::ModulesDidLoad (ModuleList &module_list)
// loading shared libraries might cause a new one to try and load
if (!m_os_ap)
LoadOperatingSystemPlugin(false);
// Give structured-data plugins a chance to see the modified modules.
for (auto pair : m_structured_data_plugin_map)
{
if (pair.second)
pair.second->ModulesDidLoad(*this, module_list);
}
}
void
@ -6598,5 +6627,129 @@ Process::GetMemoryRegions (std::vector<lldb::MemoryRegionInfoSP>& region_list)
} while (range_end != LLDB_INVALID_ADDRESS);
return error;
}
Error
Process::ConfigureStructuredData(const ConstString &type_name,
const StructuredData::ObjectSP &config_sp)
{
// If you get this, the Process-derived class needs to implement a method
// to enable an already-reported asynchronous structured data feature.
// See ProcessGDBRemote for an example implementation over gdb-remote.
return Error("unimplemented");
}
void
Process::MapSupportedStructuredDataPlugins(const StructuredData::Array
&supported_type_names)
{
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
// Bail out early if there are no type names to map.
if (supported_type_names.GetSize() == 0)
{
if (log)
log->Printf("Process::%s(): no structured data types supported",
__FUNCTION__);
return;
}
// Convert StructuredData type names to ConstString instances.
std::set<ConstString> const_type_names;
if (log)
log->Printf("Process::%s(): the process supports the following async "
"structured data types:", __FUNCTION__);
supported_type_names.ForEach([&const_type_names, &log]
(StructuredData::Object *object) {
if (!object)
{
// Invalid - shouldn't be null objects in the array.
return false;
}
auto type_name = object->GetAsString();
if (!type_name)
{
// Invalid format - all type names should be strings.
return false;
}
const_type_names.insert(ConstString(type_name->GetValue()));
if (log)
log->Printf("- %s", type_name->GetValue().c_str());
return true;
});
// For each StructuredDataPlugin, if the plugin handles any of the
// types in the supported_type_names, map that type name to that plugin.
uint32_t plugin_index = 0;
for (auto create_instance =
PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(plugin_index);
create_instance && !const_type_names.empty();
++plugin_index)
{
// Create the plugin.
StructuredDataPluginSP plugin_sp = (*create_instance)(*this);
if (!plugin_sp)
{
// This plugin doesn't think it can work with the process.
// Move on to the next.
continue;
}
// For any of the remaining type names, map any that this plugin
// supports.
std::vector<ConstString> names_to_remove;
for (auto &type_name : const_type_names)
{
if (plugin_sp->SupportsStructuredDataType(type_name))
{
m_structured_data_plugin_map.insert(std::make_pair(type_name,
plugin_sp));
names_to_remove.push_back(type_name);
if (log)
log->Printf("Process::%s(): using plugin %s for type name "
"%s", __FUNCTION__,
plugin_sp->GetPluginName().GetCString(),
type_name.GetCString());
}
}
// Remove the type names that were consumed by this plugin.
for (auto &type_name : names_to_remove)
const_type_names.erase(type_name);
}
}
bool
Process::RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp)
{
// Nothing to do if there's no data.
if (!object_sp)
return false;
// The contract is this must be a dictionary, so we can look up the
// routing key via the top-level 'type' string value within the dictionary.
StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
if (!dictionary)
return false;
// Grab the async structured type name (i.e. the feature/plugin name).
ConstString type_name;
if (!dictionary->GetValueForKeyAsString("type", type_name))
return false;
// Check if there's a plugin registered for this type name.
auto find_it = m_structured_data_plugin_map.find(type_name);
if (find_it == m_structured_data_plugin_map.end())
{
// We don't have a mapping for this structured data type.
return false;
}
// Route the structured data to the plugin.
find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp);
return true;
}

View File

@ -0,0 +1,89 @@
//===-- StructuredDataPlugin.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/Target/StructuredDataPlugin.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
using namespace lldb;
using namespace lldb_private;
namespace
{
class CommandStructuredData : public CommandObjectMultiword
{
public:
CommandStructuredData(CommandInterpreter &interpreter) :
CommandObjectMultiword(interpreter,
"structured-data",
"Parent for per-plugin structured data commands",
"plugin structured-data <plugin>")
{
}
~CommandStructuredData()
{
}
};
}
StructuredDataPlugin::StructuredDataPlugin(const ProcessWP &process_wp) :
PluginInterface(),
m_process_wp(process_wp)
{
}
StructuredDataPlugin::~StructuredDataPlugin()
{
}
bool
StructuredDataPlugin::GetEnabled(const ConstString &type_name) const
{
// By default, plugins are always enabled. Plugin authors should override
// this if there is an enabled/disabled state for their plugin.
}
ProcessSP
StructuredDataPlugin::GetProcess() const
{
return m_process_wp.lock();
}
void
StructuredDataPlugin::InitializeBasePluginForDebugger(Debugger &debugger)
{
// Create our mutliword command anchor if it doesn't already exist.
auto &interpreter = debugger.GetCommandInterpreter();
if (!interpreter.GetCommandObject("plugin structured-data"))
{
// Find the parent command.
auto parent_command =
debugger.GetCommandInterpreter().GetCommandObject("plugin");
if (!parent_command)
return;
// Create the structured-data ommand object.
auto command_name = "structured-data";
auto command_sp =
CommandObjectSP(new CommandStructuredData(interpreter));
// Hook it up under the top-level plugin command.
parent_command->LoadSubCommand(command_name,
command_sp);
}
}
void
StructuredDataPlugin::ModulesDidLoad(Process &process, ModuleList &module_list)
{
// Default implementation does nothing.
}

View File

@ -0,0 +1,119 @@
//===-- ThreadPlanCallOnFunctionExit.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/Target/ThreadPlanCallOnFunctionExit.h"
using namespace lldb;
using namespace lldb_private;
ThreadPlanCallOnFunctionExit::ThreadPlanCallOnFunctionExit(Thread &thread,
const Callback
&callback) :
ThreadPlan(ThreadPlanKind::eKindGeneric, "CallOnFunctionExit",
thread,
eVoteNoOpinion, eVoteNoOpinion // TODO check with Jim on these
),
m_callback(callback)
{
// We are not a user-generated plan.
SetIsMasterPlan(false);
}
void
ThreadPlanCallOnFunctionExit::DidPush()
{
// We now want to queue the "step out" thread plan so it executes
// and completes.
// Set stop vote to eVoteNo.
m_step_out_threadplan_sp = GetThread()
.QueueThreadPlanForStepOut(false, // abort other plans
nullptr, // addr_context
true, // first instruction
true, // stop other threads
eVoteNo, // do not say "we're stopping"
eVoteNoOpinion, // don't care about
// run state broadcasting
0, // frame_idx
eLazyBoolCalculate // avoid code w/o debinfo
);
}
// -------------------------------------------------------------------------
// ThreadPlan API
// -------------------------------------------------------------------------
void
ThreadPlanCallOnFunctionExit::GetDescription(Stream *s, lldb::DescriptionLevel
level)
{
if (!s)
return;
s->Printf("Running until completion of current function, then making "
"callback.");
}
bool
ThreadPlanCallOnFunctionExit::ValidatePlan(Stream *error)
{
// We'll say we're always good since I don't know what would make this
// invalid.
return true;
}
bool
ThreadPlanCallOnFunctionExit::ShouldStop(Event *event_ptr)
{
// If this is where we find out that an internal stop came in, then:
// Check if the step-out plan completed. If it did, then we want to
// run the callback here (our reason for living...)
if (m_step_out_threadplan_sp &&
m_step_out_threadplan_sp->IsPlanComplete())
{
m_callback();
// We no longer need the pointer to the step-out thread plan.
m_step_out_threadplan_sp.reset();
// Indicate that this plan is done and can be discarded.
SetPlanComplete();
// We're done now, but we want to return false so that we
// don't cause the thread to really stop.
}
return false;
}
bool
ThreadPlanCallOnFunctionExit::WillStop()
{
// The code looks like the return value is ignored via ThreadList::
// ShouldStop().
// This is called when we really are going to stop. We don't care
// and don't need to do anything here.
return false;
}
bool
ThreadPlanCallOnFunctionExit::DoPlanExplainsStop (Event *event_ptr)
{
// We don't ever explain a stop. The only stop that is relevant
// to us directly is the step_out plan we added to do the heavy lifting
// of getting us past the current method.
return false;
}
lldb::StateType
ThreadPlanCallOnFunctionExit::GetPlanRunState()
{
// This value doesn't matter - we'll never be the top thread plan, so
// nobody will ask us this question.
return eStateRunning;
}

View File

@ -7,6 +7,28 @@
objects = {
/* Begin PBXBuildFile section */
23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; };
23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; };
2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; };
233B4EA71D2DB54300E98261 /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; };
233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; };
23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; };
23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; };
23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; };
23562ED71D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; };
23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; };
23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; };
237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; };
23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; };
23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; };
23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; };
23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; };
23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; };
23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; };
23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; };
23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; };
23D1B0291D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; };
23D1B02A1D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; };
264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; };
2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; };
266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; };
@ -82,6 +104,33 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
233B4EA51D2DB54300E98261 /* JSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSON.cpp; sourceTree = "<group>"; };
233B4EA61D2DB54300E98261 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSON.h; sourceTree = "<group>"; };
233B4EA81D2DB96A00E98261 /* StringConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringConvert.cpp; path = ../../../source/Host/common/StringConvert.cpp; sourceTree = "<group>"; };
23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogTypes.h; sourceTree = "<group>"; };
23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessageOsLog.cpp; sourceTree = "<group>"; };
23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogMessageOsLog.h; sourceTree = "<group>"; };
23562ED41D3426DD00AB2BD4 /* ActivityStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStore.h; sourceTree = "<group>"; };
23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActivityStore.cpp; sourceTree = "<group>"; };
23562ED81D342B0000AB2BD4 /* LogMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessage.cpp; sourceTree = "<group>"; };
237821AD1D4917D20028B7A1 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterExactMatch.cpp; sourceTree = "<group>"; };
237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterExactMatch.h; sourceTree = "<group>"; };
23AC04C41D2F41A00072351D /* LogFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilter.cpp; sourceTree = "<group>"; };
23AC04C51D2F41A00072351D /* LogFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilter.h; sourceTree = "<group>"; };
23AC04C81D2F42250072351D /* LogFilterChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterChain.cpp; sourceTree = "<group>"; };
23AC04C91D2F42250072351D /* LogFilterChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterChain.h; sourceTree = "<group>"; };
23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogInterfaces.h; sourceTree = "<group>"; };
23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterRegex.cpp; sourceTree = "<group>"; };
23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterRegex.h; sourceTree = "<group>"; };
23AC04D11D2F60130072351D /* LogMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogMessage.h; sourceTree = "<group>"; };
23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DarwinLogCollector.cpp; sourceTree = "<group>"; };
23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinLogCollector.h; sourceTree = "<group>"; };
23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStreamSPI.h; sourceTree = "<group>"; };
23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogEvent.h; sourceTree = "<group>"; };
23D1B0271D497E8B00FF831B /* OsLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OsLogger.cpp; sourceTree = "<group>"; };
23D1B0281D497E8B00FF831B /* OsLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OsLogger.h; sourceTree = "<group>"; };
260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBRuntimeAction.h; sourceTree = "<group>"; };
260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBThreadResumeActions.cpp; sourceTree = "<group>"; };
260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBThreadResumeActions.h; sourceTree = "<group>"; };
@ -217,6 +266,34 @@
name = Products;
sourceTree = "<group>";
};
23AC04C31D2F3E9A0072351D /* DarwinLog */ = {
isa = PBXGroup;
children = (
237821AD1D4917D20028B7A1 /* CMakeLists.txt */,
23562ED41D3426DD00AB2BD4 /* ActivityStore.h */,
23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */,
23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */,
23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */,
23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */,
23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */,
23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */,
23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */,
23AC04C51D2F41A00072351D /* LogFilter.h */,
23AC04C41D2F41A00072351D /* LogFilter.cpp */,
23AC04C91D2F42250072351D /* LogFilterChain.h */,
23AC04C81D2F42250072351D /* LogFilterChain.cpp */,
237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */,
237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */,
23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */,
23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */,
23AC04D11D2F60130072351D /* LogMessage.h */,
23562ED81D342B0000AB2BD4 /* LogMessage.cpp */,
23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */,
23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */,
);
path = DarwinLog;
sourceTree = "<group>";
};
266B5ECE1460A68200E43F0A /* arm64 */ = {
isa = PBXGroup;
children = (
@ -273,6 +350,8 @@
26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */,
260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */,
260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */,
233B4EA61D2DB54300E98261 /* JSON.h */,
233B4EA51D2DB54300E98261 /* JSON.cpp */,
264F679A1B2F9EB200140093 /* JSONGenerator.h */,
AF67AC000D34604D0022D128 /* PseudoTerminal.h */,
AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */,
@ -281,6 +360,7 @@
26C637FE0C71334A0024798E /* PThreadEvent.cpp */,
26C638000C71334A0024798E /* PThreadMutex.h */,
2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */,
233B4EA81D2DB96A00E98261 /* StringConvert.cpp */,
26C638020C71334A0024798E /* SysSignal.h */,
26C638010C71334A0024798E /* SysSignal.cpp */,
26C638060C71334A0024798E /* TTYState.h */,
@ -315,13 +395,13 @@
26C637E60C71334A0024798E /* MacOSX */ = {
isa = PBXGroup;
children = (
AF0934BA18E12B92005A11FD /* Genealogy.h */,
AF0934BB18E12B92005A11FD /* GenealogySPI.h */,
23AC04C31D2F3E9A0072351D /* DarwinLog */,
2695DD920D3EBFF6007E4CA2 /* CFBundle.h */,
2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */,
2695DD9A0D3EC160007E4CA2 /* CFString.h */,
2695DD9B0D3EC160007E4CA2 /* CFString.cpp */,
26C637E70C71334A0024798E /* CFUtils.h */,
2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */,
2675D41C0CCEB6CF000F49AF /* arm */,
266B5ECE1460A68200E43F0A /* arm64 */,
26C637E90C71334A0024798E /* i386 */,
@ -331,6 +411,8 @@
4971AE7113D10F4F00649E37 /* HasAVX.s */,
26C637E80C71334A0024798E /* dbgnub-mig.defs */,
AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */,
AF0934BA18E12B92005A11FD /* Genealogy.h */,
AF0934BB18E12B92005A11FD /* GenealogySPI.h */,
26C637EF0C71334A0024798E /* MachException.h */,
26C637EE0C71334A0024798E /* MachException.cpp */,
26C637F10C71334A0024798E /* MachProcess.h */,
@ -345,6 +427,8 @@
26C637F80C71334A0024798E /* MachVMRegion.cpp */,
26B67DE00EE9BC30006C8BC0 /* MachTask.h */,
26B67DE10EE9BC30006C8BC0 /* MachTask.mm */,
23D1B0281D497E8B00FF831B /* OsLogger.h */,
23D1B0271D497E8B00FF831B /* OsLogger.cpp */,
9457ECF61419864100DFE7D8 /* stack_logging.h */,
);
path = MacOSX;
@ -490,35 +574,47 @@
26CE05A9115C36250022F371 /* debugserver.cpp in Sources */,
26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */,
26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */,
23D1B0291D497E8B00FF831B /* OsLogger.cpp in Sources */,
26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */,
26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */,
26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */,
26CE05B0115C36340022F371 /* MachException.cpp in Sources */,
26CE05B1115C36350022F371 /* MachProcess.mm in Sources */,
26CE05B2115C36360022F371 /* MachThread.cpp in Sources */,
233B4EA71D2DB54300E98261 /* JSON.cpp in Sources */,
26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */,
26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */,
26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */,
26CE05B6115C36390022F371 /* MachTask.mm in Sources */,
26CE05B7115C363B0022F371 /* DNB.cpp in Sources */,
AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */,
23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */,
233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */,
23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */,
26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */,
26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */,
23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */,
23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */,
26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */,
23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */,
26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */,
26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */,
26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */,
26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */,
23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */,
26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */,
26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */,
26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */,
26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */,
26CE05C3115C36580022F371 /* CFString.cpp in Sources */,
26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */,
26CE05C3115C36580022F371 /* CFString.cpp in Sources */,
26CE05C4115C36590022F371 /* CFData.cpp in Sources */,
23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */,
26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */,
2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */,
264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */,
4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */,
237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp in Sources */,
266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -530,37 +626,49 @@
456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */,
456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */,
456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */,
23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */,
456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */,
23D1B02A1D497E8B00FF831B /* OsLogger.cpp in Sources */,
456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */,
456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */,
456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */,
456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */,
456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */,
456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */,
2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */,
456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */,
456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */,
456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */,
456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */,
23562ED71D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */,
456F67541AD46CE9002850C2 /* MachTask.mm in Sources */,
456F67551AD46CE9002850C2 /* DNB.cpp in Sources */,
456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */,
456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */,
456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */,
456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */,
23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */,
456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */,
456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */,
456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */,
456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */,
23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */,
456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */,
23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */,
456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */,
456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */,
23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */,
456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
456F67621AD46CE9002850C2 /* CFString.cpp in Sources */,
456F67631AD46CE9002850C2 /* CFData.cpp in Sources */,
23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */,
456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */,
456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */,
456F67661AD46CE9002850C2 /* StringExtractor.cpp in Sources */,
456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */,
456F67681AD46CE9002850C2 /* HasAVX.s in Sources */,
23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */,
456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -600,6 +708,8 @@
"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
LLDB_COMPRESSION_LDFLAGS = "";
"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
LLDB_ZLIB_LDFLAGS = "-lz";
MACOSX_DEPLOYMENT_TARGET = 10.10;
@ -642,6 +752,8 @@
"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
LLDB_COMPRESSION_LDFLAGS = "";
"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
LLDB_ZLIB_LDFLAGS = "-lz";
MACOSX_DEPLOYMENT_TARGET = 10.10;
@ -682,6 +794,8 @@
"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
LLDB_COMPRESSION_LDFLAGS = "";
"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 1;
LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
LLDB_ZLIB_LDFLAGS = "-lz";
MACOSX_DEPLOYMENT_TARGET = 10.10;
@ -743,6 +857,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*]" = (
"-Wparentheses",
@ -752,6 +867,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -843,6 +959,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -852,6 +969,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -942,6 +1060,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -951,6 +1070,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -1050,6 +1170,7 @@
"-Wparentheses",
"-DOS_OBJECT_USE_OBJC=0",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -1119,6 +1240,7 @@
"-Wparentheses",
"-DOS_OBJECT_USE_OBJC=0",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -1184,11 +1306,13 @@
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
"-DOS_OBJECT_USE_OBJC=0",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -1255,12 +1379,15 @@
"-Wparentheses",
"$(LLDB_ENERGY_CFLAGS)",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*]" = (
"-Wparentheses",
"-DOS_OBJECT_USE_OBJC=0",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
"-framework",
@ -1312,6 +1439,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
@ -1374,6 +1503,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -1383,6 +1513,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
@ -1451,6 +1582,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
@ -1512,6 +1645,7 @@
"$(LLDB_ENERGY_CFLAGS)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -1521,6 +1655,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
@ -1595,11 +1730,13 @@
"-Wparentheses",
"$(LLDB_ENERGY_CFLAGS)",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
"-DOS_OBJECT_USE_OBJC=0",
"-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
@ -1669,6 +1806,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
@ -1721,6 +1860,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -1730,6 +1870,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "";
@ -1798,6 +1939,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
LLDB_USE_OS_LOG = 0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
@ -1851,6 +1994,7 @@
"-DDT_VARIANT_$(DT_VARIANT)",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
"-Wparentheses",
@ -1860,6 +2004,7 @@
"-DOS_OBJECT_USE_OBJC=0",
"$(LLDB_COMPRESSION_CFLAGS)",
"$(LLDB_ZLIB_CFLAGS)",
"$(LLDB_OS_LOG_CFLAGS)",
);
"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (

View File

@ -1,5 +1,6 @@
include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
include_directories(${LLDB_SOURCE_DIR}/source)
include_directories(MacOSX/DarwinLog)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
include_directories(MacOSX)
@ -42,6 +43,11 @@ add_library(lldbDebugserverCommon
DNBLog.cpp
DNBRegisterInfo.cpp
DNBThreadResumeActions.cpp
JSON.cpp
# JSON reader depends on the following LLDB-common files
${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp
${LLDB_SOURCE_DIR}/source/Utility/StringExtractor.cpp
# end JSON reader dependencies
libdebugserver.cpp
PseudoTerminal.cpp
PThreadEvent.cpp

View File

@ -41,6 +41,7 @@
#endif
#endif
#include "MacOSX/DarwinLog/DarwinLogCollector.h"
#include "MacOSX/MachProcess.h"
#include "MacOSX/MachTask.h"
#include "MacOSX/Genealogy.h"
@ -1868,6 +1869,12 @@ DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_
return 0;
}
DarwinLogEventVector
DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid)
{
return DarwinLogCollector::GetEventsForProcess(pid);
}
nub_size_t
DNBProcessGetStopCount (nub_process_t pid)
{

View File

@ -14,6 +14,7 @@
#ifndef __DNB_h__
#define __DNB_h__
#include "MacOSX/DarwinLog/DarwinLogEvent.h"
#include "MacOSX/Genealogy.h"
#include "MacOSX/ThreadInfo.h"
#include "JSONGenerator.h"
@ -81,6 +82,8 @@ nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr)
int DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT;
std::string DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT;
nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) DNB_EXPORT;
DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid);
//----------------------------------------------------------------------
// Process status

View File

@ -142,6 +142,7 @@ enum
#define LOG_WATCHPOINTS (1u << 11)
#define LOG_STEP (1u << 12)
#define LOG_TASK (1u << 13)
#define LOG_DARWIN_LOG (1u << 14)
#define LOG_LO_USER (1u << 16)
#define LOG_HI_USER (1u << 31)
#define LOG_ALL 0xFFFFFFFFu

View File

@ -0,0 +1,746 @@
//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "JSON.h"
// C includes
#include <assert.h>
#include <limits.h>
// C++ includes
#include <iomanip>
#include <sstream>
#include "lldb/Host/StringConvert.h"
using namespace lldb_private;
std::string
JSONString::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;
}
JSONString::JSONString () :
JSONValue(JSONValue::Kind::String),
m_data()
{
}
JSONString::JSONString (const char* s) :
JSONValue(JSONValue::Kind::String),
m_data(s ? s : "")
{
}
JSONString::JSONString (const std::string& s) :
JSONValue(JSONValue::Kind::String),
m_data(s)
{
}
void
JSONString::Write (std::ostream& s)
{
s << "\"" << json_string_quote_metachars(m_data).c_str() <<"\"";
}
uint64_t
JSONNumber::GetAsUnsigned() const
{
switch (m_data_type)
{
case DataType::Unsigned:
return m_data.m_unsigned;
case DataType::Signed:
return (uint64_t)m_data.m_signed;
case DataType::Double:
return (uint64_t)m_data.m_double;
}
assert("Unhandled data type");
}
int64_t
JSONNumber::GetAsSigned() const
{
switch (m_data_type)
{
case DataType::Unsigned:
return (int64_t)m_data.m_unsigned;
case DataType::Signed:
return m_data.m_signed;
case DataType::Double:
return (int64_t)m_data.m_double;
}
assert("Unhandled data type");
}
double
JSONNumber::GetAsDouble() const
{
switch (m_data_type)
{
case DataType::Unsigned:
return (double)m_data.m_unsigned;
case DataType::Signed:
return (double)m_data.m_signed;
case DataType::Double:
return m_data.m_double;
}
assert("Unhandled data type");
}
void
JSONNumber::Write (std::ostream& s)
{
switch (m_data_type)
{
case DataType::Unsigned:
s << m_data.m_unsigned;
break;
case DataType::Signed:
s << m_data.m_signed;
break;
case DataType::Double:
// Set max precision to emulate %g.
s << std::setprecision(std::numeric_limits<double>::digits10 + 1);
s << m_data.m_double;
break;
}
}
JSONTrue::JSONTrue () :
JSONValue(JSONValue::Kind::True)
{
}
void
JSONTrue::Write(std::ostream& s)
{
s << "true";
}
JSONFalse::JSONFalse () :
JSONValue(JSONValue::Kind::False)
{
}
void
JSONFalse::Write(std::ostream& s)
{
s << "false";
}
JSONNull::JSONNull () :
JSONValue(JSONValue::Kind::Null)
{
}
void
JSONNull::Write(std::ostream& s)
{
s << "null";
}
JSONObject::JSONObject () :
JSONValue(JSONValue::Kind::Object)
{
}
void
JSONObject::Write (std::ostream& s)
{
bool first = true;
s << '{';
auto iter = m_elements.begin(), end = m_elements.end();
for (;iter != end; iter++)
{
if (first)
first = false;
else
s << ',';
JSONString key(iter->first);
JSONValue::SP value(iter->second);
key.Write(s);
s << ':';
value->Write(s);
}
s << '}';
}
bool
JSONObject::SetObject (const std::string& key,
JSONValue::SP value)
{
if (key.empty() || nullptr == value.get())
return false;
m_elements[key] = value;
return true;
}
JSONValue::SP
JSONObject::GetObject (const std::string& key) const
{
auto iter = m_elements.find(key), end = m_elements.end();
if (iter == end)
return JSONValue::SP();
return iter->second;
}
bool
JSONObject::GetObjectAsBool (const std::string& key, bool& value) const
{
auto value_sp = GetObject(key);
if (!value_sp)
{
// The given key doesn't exist, so we have no value.
return false;
}
if (JSONTrue::classof(value_sp.get()))
{
// We have the value, and it is true.
value = true;
return true;
}
else if (JSONFalse::classof(value_sp.get()))
{
// We have the value, and it is false.
value = false;
return true;
}
else
{
// We don't have a valid bool value for the given key.
return false;
}
}
bool
JSONObject::GetObjectAsString (const std::string& key, std::string& value) const
{
auto value_sp = GetObject(key);
if (!value_sp)
{
// The given key doesn't exist, so we have no value.
return false;
}
if (!JSONString::classof(value_sp.get()))
return false;
value = static_cast<JSONString*>(value_sp.get())->GetData();
return true;
}
JSONArray::JSONArray () :
JSONValue(JSONValue::Kind::Array)
{
}
void
JSONArray::Write (std::ostream& s)
{
bool first = true;
s << '[';
auto iter = m_elements.begin(), end = m_elements.end();
for (;iter != end; iter++)
{
if (first)
first = false;
else
s << ',';
(*iter)->Write(s);
}
s << ']';
}
bool
JSONArray::SetObject (Index i,
JSONValue::SP value)
{
if (value.get() == nullptr)
return false;
if (i < m_elements.size())
{
m_elements[i] = value;
return true;
}
if (i == m_elements.size())
{
m_elements.push_back(value);
return true;
}
return false;
}
bool
JSONArray::AppendObject (JSONValue::SP value)
{
if (value.get() == nullptr)
return false;
m_elements.push_back(value);
return true;
}
JSONValue::SP
JSONArray::GetObject (Index i)
{
if (i < m_elements.size())
return m_elements[i];
return JSONValue::SP();
}
JSONArray::Size
JSONArray::GetNumElements ()
{
return m_elements.size();
}
JSONParser::JSONParser (const char *cstr) :
StringExtractor(cstr)
{
}
JSONParser::Token
JSONParser::GetToken (std::string &value)
{
std::ostringstream error;
value.clear();
SkipSpaces ();
const uint64_t start_index = m_index;
const char ch = GetChar();
switch (ch)
{
case '{': return Token::ObjectStart;
case '}': return Token::ObjectEnd;
case '[': return Token::ArrayStart;
case ']': return Token::ArrayEnd;
case ',': return Token::Comma;
case ':': return Token::Colon;
case '\0': return Token::EndOfFile;
case 't':
if (GetChar() == 'r')
if (GetChar() == 'u')
if (GetChar() == 'e')
return Token::True;
break;
case 'f':
if (GetChar() == 'a')
if (GetChar() == 'l')
if (GetChar() == 's')
if (GetChar() == 'e')
return Token::False;
break;
case 'n':
if (GetChar() == 'u')
if (GetChar() == 'l')
if (GetChar() == 'l')
return Token::Null;
break;
case '"':
{
while (1)
{
bool was_escaped = false;
int escaped_ch = GetEscapedChar(was_escaped);
if (escaped_ch == -1)
{
error << "error: an error occurred getting a character from offset " <<start_index;
value = std::move(error.str());
return Token::Error;
}
else
{
const bool is_end_quote = escaped_ch == '"';
const bool is_null = escaped_ch == 0;
if (was_escaped || (!is_end_quote && !is_null))
{
if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX)
{
value.append(1, (char)escaped_ch);
}
else
{
error << "error: wide character support is needed for unicode character 0x" << std::setprecision(4) << std::hex << escaped_ch;
error << " at offset " << start_index;
value = std::move(error.str());
return Token::Error;
}
}
else if (is_end_quote)
{
return Token::String;
}
else if (is_null)
{
value = "error: missing end quote for string";
return Token::Error;
}
}
}
}
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
bool done = false;
bool got_decimal_point = false;
uint64_t exp_index = 0;
bool got_int_digits = (ch >= '0') && (ch <= '9');
bool got_frac_digits = false;
bool got_exp_digits = false;
while (!done)
{
const char next_ch = PeekChar();
switch (next_ch)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (exp_index != 0)
{
got_exp_digits = true;
}
else if (got_decimal_point)
{
got_frac_digits = true;
}
else
{
got_int_digits = true;
}
++m_index; // Skip this character
break;
case '.':
if (got_decimal_point)
{
error << "error: extra decimal point found at offset " << start_index;
value = std::move(error.str());
return Token::Error;
}
else
{
got_decimal_point = true;
++m_index; // Skip this character
}
break;
case 'e':
case 'E':
if (exp_index != 0)
{
error << "error: extra exponent character found at offset " << start_index;
value = std::move(error.str());
return Token::Error;
}
else
{
exp_index = m_index;
++m_index; // Skip this character
}
break;
case '+':
case '-':
// The '+' and '-' can only come after an exponent character...
if (exp_index == m_index - 1)
{
++m_index; // Skip the exponent sign character
}
else
{
error << "error: unexpected " << next_ch << " character at offset " << start_index;
value = std::move(error.str());
return Token::Error;
}
break;
default:
done = true;
break;
}
}
if (m_index > start_index)
{
value = m_packet.substr(start_index, m_index - start_index);
if (got_decimal_point)
{
if (exp_index != 0)
{
// We have an exponent, make sure we got exponent digits
if (got_exp_digits)
{
return Token::Float;
}
else
{
error << "error: got exponent character but no exponent digits at offset in float value \"" << value.c_str() << "\"";
value = std::move(error.str());
return Token::Error;
}
}
else
{
// No exponent, but we need at least one decimal after the decimal point
if (got_frac_digits)
{
return Token::Float;
}
else
{
error << "error: no digits after decimal point \"" << value.c_str() << "\"";
value = std::move(error.str());
return Token::Error;
}
}
}
else
{
// No decimal point
if (got_int_digits)
{
// We need at least some integer digits to make an integer
return Token::Integer;
}
else
{
error << "error: no digits negate sign \"" << value.c_str() << "\"";
value = std::move(error.str());
return Token::Error;
}
}
}
else
{
error << "error: invalid number found at offset " << start_index;
value = std::move(error.str());
return Token::Error;
}
}
break;
default:
break;
}
error << "error: failed to parse token at offset " << start_index << " (around character '" << ch << "')";
value = std::move(error.str());
return Token::Error;
}
int
JSONParser::GetEscapedChar(bool &was_escaped)
{
was_escaped = false;
const char ch = GetChar();
if (ch == '\\')
{
was_escaped = true;
const char ch2 = GetChar();
switch (ch2)
{
case '"':
case '\\':
case '/':
default:
break;
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'u':
{
const int hi_byte = DecodeHexU8();
const int lo_byte = DecodeHexU8();
if (hi_byte >=0 && lo_byte >= 0)
return hi_byte << 8 | lo_byte;
return -1;
}
break;
}
return ch2;
}
return ch;
}
JSONValue::SP
JSONParser::ParseJSONObject ()
{
// The "JSONParser::Token::ObjectStart" token should have already been consumed
// by the time this function is called
std::unique_ptr<JSONObject> dict_up(new JSONObject());
std::string value;
std::string key;
while (1)
{
JSONParser::Token token = GetToken(value);
if (token == JSONParser::Token::String)
{
key.swap(value);
token = GetToken(value);
if (token == JSONParser::Token::Colon)
{
JSONValue::SP value_sp = ParseJSONValue();
if (value_sp)
dict_up->SetObject(key, value_sp);
else
break;
}
}
else if (token == JSONParser::Token::ObjectEnd)
{
return JSONValue::SP(dict_up.release());
}
else if (token == JSONParser::Token::Comma)
{
continue;
}
else
{
break;
}
}
return JSONValue::SP();
}
JSONValue::SP
JSONParser::ParseJSONArray ()
{
// The "JSONParser::Token::ObjectStart" token should have already been consumed
// by the time this function is called
std::unique_ptr<JSONArray> array_up(new JSONArray());
std::string value;
std::string key;
while (1)
{
JSONValue::SP value_sp = ParseJSONValue();
if (value_sp)
array_up->AppendObject(value_sp);
else
break;
JSONParser::Token token = GetToken(value);
if (token == JSONParser::Token::Comma)
{
continue;
}
else if (token == JSONParser::Token::ArrayEnd)
{
return JSONValue::SP(array_up.release());
}
else
{
break;
}
}
return JSONValue::SP();
}
JSONValue::SP
JSONParser::ParseJSONValue ()
{
std::string value;
const JSONParser::Token token = GetToken(value);
switch (token)
{
case JSONParser::Token::ObjectStart:
return ParseJSONObject();
case JSONParser::Token::ArrayStart:
return ParseJSONArray();
case JSONParser::Token::Integer:
{
if (value.front() == '-')
{
bool success = false;
int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success);
if (success)
return JSONValue::SP(new JSONNumber(sval));
}
else
{
bool success = false;
uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success);
if (success)
return JSONValue::SP(new JSONNumber(uval));
}
}
break;
case JSONParser::Token::Float:
{
bool success = false;
double val = StringConvert::ToDouble(value.c_str(), 0.0, &success);
if (success)
return JSONValue::SP(new JSONNumber(val));
}
break;
case JSONParser::Token::String:
return JSONValue::SP(new JSONString(value));
case JSONParser::Token::True:
return JSONValue::SP(new JSONTrue());
case JSONParser::Token::False:
return JSONValue::SP(new JSONFalse());
case JSONParser::Token::Null:
return JSONValue::SP(new JSONNull());
default:
break;
}
return JSONValue::SP();
}

View File

@ -0,0 +1,382 @@
//===---------------------JSON.h --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef utility_JSON_h_
#define utility_JSON_h_
// This cross-project usage is fine as StringExtractor.h is entirely
// self-contained.
#include "lldb/Utility/StringExtractor.h"
// C includes
#include <inttypes.h>
#include <stdint.h>
// C++ includes
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
class JSONValue
{
public:
virtual void
Write (std::ostream& s) = 0;
typedef std::shared_ptr<JSONValue> SP;
enum class Kind
{
String,
Number,
True,
False,
Null,
Object,
Array
};
JSONValue (Kind k) :
m_kind(k)
{}
Kind
GetKind() const
{
return m_kind;
}
virtual
~JSONValue () = default;
private:
const Kind m_kind;
};
class JSONString : public JSONValue
{
public:
JSONString ();
JSONString (const char* s);
JSONString (const std::string& s);
JSONString (const JSONString& s) = delete;
JSONString&
operator = (const JSONString& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONString> SP;
std::string
GetData () { return m_data; }
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::String;
}
~JSONString() override = default;
private:
static std::string
json_string_quote_metachars (const std::string&);
std::string m_data;
};
class JSONNumber : public JSONValue
{
public:
typedef std::shared_ptr<JSONNumber> SP;
// We cretae a constructor for all integer and floating point type with using templates and
// SFINAE to avoid having ambiguous overloads because of the implicit type promotion. If we
// would have constructors only with int64_t, uint64_t and double types then constructing a
// JSONNumber from an int32_t (or any other similar type) would fail to compile.
template <typename T,
typename std::enable_if<std::is_integral<T>::value &&
std::is_unsigned<T>::value>::type* = nullptr>
explicit JSONNumber (T u) :
JSONValue(JSONValue::Kind::Number),
m_data_type(DataType::Unsigned)
{
m_data.m_unsigned = u;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value &&
std::is_signed<T>::value>::type* = nullptr>
explicit JSONNumber (T s) :
JSONValue(JSONValue::Kind::Number),
m_data_type(DataType::Signed)
{
m_data.m_signed = s;
}
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
explicit JSONNumber (T d) :
JSONValue(JSONValue::Kind::Number),
m_data_type(DataType::Double)
{
m_data.m_double = d;
}
~JSONNumber() override = default;
JSONNumber (const JSONNumber& s) = delete;
JSONNumber&
operator = (const JSONNumber& s) = delete;
void
Write(std::ostream& s) override;
uint64_t
GetAsUnsigned() const;
int64_t
GetAsSigned() const;
double
GetAsDouble() const;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::Number;
}
private:
enum class DataType : uint8_t
{
Unsigned,
Signed,
Double
} m_data_type;
union
{
uint64_t m_unsigned;
int64_t m_signed;
double m_double;
} m_data;
};
class JSONTrue : public JSONValue
{
public:
JSONTrue ();
JSONTrue (const JSONTrue& s) = delete;
JSONTrue&
operator = (const JSONTrue& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONTrue> SP;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::True;
}
~JSONTrue() override = default;
};
class JSONFalse : public JSONValue
{
public:
JSONFalse ();
JSONFalse (const JSONFalse& s) = delete;
JSONFalse&
operator = (const JSONFalse& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONFalse> SP;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::False;
}
~JSONFalse() override = default;
};
class JSONNull : public JSONValue
{
public:
JSONNull ();
JSONNull (const JSONNull& s) = delete;
JSONNull&
operator = (const JSONNull& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONNull> SP;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::Null;
}
~JSONNull() override = default;
};
class JSONObject : public JSONValue
{
public:
JSONObject ();
JSONObject (const JSONObject& s) = delete;
JSONObject&
operator = (const JSONObject& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONObject> SP;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::Object;
}
bool
SetObject (const std::string& key,
JSONValue::SP value);
JSONValue::SP
GetObject (const std::string& key) const;
// -------------------------------------------------------------------------
/// Return keyed value as bool
///
/// @param[in] key
/// The value of the key to lookup
///
/// @param[out] value
/// The value of the key as a bool. Undefined if the key doesn't
/// exist or if the key is not either true or false.
///
/// @return
/// true if the key existed as was a bool value; false otherwise.
/// Note the return value is *not* the value of the bool, use
/// \b value for that.
// -------------------------------------------------------------------------
bool
GetObjectAsBool (const std::string& key, bool& value) const;
bool
GetObjectAsString (const std::string& key, std::string& value) const;
~JSONObject() override = default;
private:
typedef std::map<std::string, JSONValue::SP> Map;
typedef Map::iterator Iterator;
Map m_elements;
};
class JSONArray : public JSONValue
{
public:
JSONArray ();
JSONArray (const JSONArray& s) = delete;
JSONArray&
operator = (const JSONArray& s) = delete;
void
Write(std::ostream& s) override;
typedef std::shared_ptr<JSONArray> SP;
static bool classof(const JSONValue *V)
{
return V->GetKind() == JSONValue::Kind::Array;
}
private:
typedef std::vector<JSONValue::SP> Vector;
typedef Vector::iterator Iterator;
typedef Vector::size_type Index;
typedef Vector::size_type Size;
public:
bool
SetObject (Index i,
JSONValue::SP value);
bool
AppendObject (JSONValue::SP value);
JSONValue::SP
GetObject (Index i);
Size
GetNumElements ();
~JSONArray() override = default;
Vector m_elements;
};
class JSONParser : public StringExtractor
{
public:
enum Token
{
Invalid,
Error,
ObjectStart,
ObjectEnd,
ArrayStart,
ArrayEnd,
Comma,
Colon,
String,
Integer,
Float,
True,
False,
Null,
EndOfFile
};
JSONParser (const char *cstr);
int
GetEscapedChar (bool &was_escaped);
Token
GetToken (std::string &value);
JSONValue::SP
ParseJSONValue ();
protected:
JSONValue::SP
ParseJSONObject ();
JSONValue::SP
ParseJSONArray ();
};
#endif // utility_JSON_h_

View File

@ -3,6 +3,7 @@
add_subdirectory(i386)
#add_subdirectory(ppc)
add_subdirectory(x86_64)
add_subdirectory(DarwinLog)
include_directories(..)
@ -32,6 +33,7 @@ set(DEBUGSERVER_USED_LIBS
lldbUtility
lldbDebugserverMacOSX_I386
lldbDebugserverMacOSX_X86_64
lldbDebugserverMacOSX_DarwinLog
)
add_lldb_executable(debugserver
@ -46,6 +48,7 @@ add_lldb_executable(debugserver
MachThreadList.cpp
MachVMMemory.cpp
MachVMRegion.cpp
OsLogger.cpp
${generated_mach_interfaces}
${DEBUGSERVER_VERS_GENERATED_FILE}
)

Some files were not shown because too many files have changed in this diff Show More