52 changes: 52 additions & 0 deletions lldb/include/lldb/API/SBStructuredData.h
Original file line number Diff line number Diff line change
@@ -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 */
9 changes: 4 additions & 5 deletions lldb/include/lldb/Core/Broadcaster.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,10 @@ class BroadcasterManager :
/// //----------------------------------------------------------
/// 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
//----------------------------------------------------------------------
Expand Down
75 changes: 75 additions & 0 deletions lldb/include/lldb/Core/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -158,6 +159,80 @@ class EventDataReceipt : public EventData
}
};

//----------------------------------------------------------------------
/// 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
//----------------------------------------------------------------------
Expand Down
69 changes: 69 additions & 0 deletions lldb/include/lldb/Core/PluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,65 @@ class PluginManager
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
//------------------------------------------------------------------
Expand Down Expand Up @@ -531,6 +590,16 @@ class PluginManager
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
Expand Down
19 changes: 18 additions & 1 deletion lldb/include/lldb/Interpreter/Args.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,17 @@ class Args
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,
Expand All @@ -460,12 +471,18 @@ class Args
/// @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:
//------------------------------------------------------------------
Expand Down
128 changes: 125 additions & 3 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,8 @@ class Process :
eBroadcastBitInterrupt = (1 << 1),
eBroadcastBitSTDOUT = (1 << 2),
eBroadcastBitSTDERR = (1 << 3),
eBroadcastBitProfileData = (1 << 4)
eBroadcastBitProfileData = (1 << 4),
eBroadcastBitStructuredData = (1 << 5),
};

enum
Expand Down Expand Up @@ -3254,6 +3255,71 @@ class Process :
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);
Expand Down Expand Up @@ -3391,6 +3457,57 @@ class Process :
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
//------------------------------------------------------------------
Expand All @@ -3408,6 +3525,9 @@ class Process :
{
}
};

using StructuredDataPluginMap = std::map<ConstString,
lldb::StructuredDataPluginSP>;

//------------------------------------------------------------------
// Member variables
Expand Down Expand Up @@ -3477,7 +3597,9 @@ class Process :
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,
Expand Down Expand Up @@ -3562,7 +3684,7 @@ class Process :

void
BroadcastAsyncProfileData(const std::string &one_profile_data);

static void
STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len);

Expand Down
204 changes: 204 additions & 0 deletions lldb/include/lldb/Target/StructuredDataPlugin.h
Original file line number Diff line number Diff line change
@@ -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
69 changes: 69 additions & 0 deletions lldb/include/lldb/Target/ThreadPlanCallOnFunctionExit.h
Original file line number Diff line number Diff line change
@@ -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 */
7 changes: 7 additions & 0 deletions lldb/include/lldb/lldb-forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Error;
class EvaluateExpressionOptions;
class Event;
class EventData;
class EventDataStructuredData;
class ExecutionContext;
class ExecutionContextRef;
class ExecutionContextRefLocker;
Expand Down Expand Up @@ -215,6 +216,7 @@ class StreamFile;
class StreamString;
class StringList;
struct StringSummaryFormat;
class StructuredDataPlugin;
class SystemRuntime;
class TypeSummaryImpl;
class TypeSummaryOptions;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/lldb-private-interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
74 changes: 72 additions & 2 deletions lldb/lldb.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

430 changes: 430 additions & 0 deletions lldb/packages/Python/lldbsuite/test/darwin_log.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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.")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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.")
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LEVEL = ../../../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
Original file line number Diff line number Diff line change
@@ -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")
Loading