rdar://problem/11457143 [ER] need "watchpoint command ..."

Add 'watchpoint command add/delete/list' to lldb, plus two .py test files.

llvm-svn: 161638
This commit is contained in:
Johnny Chen
2012-08-09 23:09:42 +00:00
parent 637ff0cc0f
commit e9a5627e7a
20 changed files with 2092 additions and 44 deletions

View File

@@ -142,8 +142,8 @@ public:
//------------------------------------------------------------------
/// Used in InvokeCallback to tell whether it is the right time to run this kind of callback.
///
/// @param[in] condition
/// The condition expression to evaluate when the breakpoint is hit.
/// @return
/// The synchronicity of our callback.
//------------------------------------------------------------------
bool IsCallbackSynchronous () {
return m_callback_is_synchronous;

View File

@@ -22,6 +22,7 @@
#include "lldb/lldb-private.h"
#include "lldb/Target/Target.h"
#include "lldb/Core/UserID.h"
#include "lldb/Breakpoint/WatchpointOptions.h"
#include "lldb/Breakpoint/StoppointLocation.h"
namespace lldb_private {
@@ -52,8 +53,6 @@ public:
uint32_t GetIgnoreCount () const;
void SetIgnoreCount (uint32_t n);
void SetWatchpointType (uint32_t type);
bool SetCallback (WatchpointHitCallback callback, void *callback_baton);
void ClearCallback();
void SetDeclInfo (std::string &str);
void SetWatchSpec (std::string &str);
void GetDescription (Stream *s, lldb::DescriptionLevel level);
@@ -62,6 +61,43 @@ public:
Target &GetTarget() { return *m_target; }
const Error &GetError() { return m_error; }
//------------------------------------------------------------------
/// Returns the WatchpointOptions structure set for this watchpoint.
///
/// @return
/// A pointer to this watchpoint's WatchpointOptions.
//------------------------------------------------------------------
WatchpointOptions *
GetOptions () { return &m_options; }
//------------------------------------------------------------------
/// Set the callback action invoked when the watchpoint is hit.
///
/// @param[in] callback
/// The method that will get called when the watchpoint is hit.
/// @param[in] callback_baton
/// A void * pointer that will get passed back to the callback function.
/// @param[in] is_synchronous
/// If \b true the callback will be run on the private event thread
/// before the stop event gets reported. If false, the callback will get
/// handled on the public event thead after the stop has been posted.
///
/// @return
/// \b true if the process should stop when you hit the watchpoint.
/// \b false if it should continue.
//------------------------------------------------------------------
void
SetCallback (WatchpointHitCallback callback,
void *callback_baton,
bool is_synchronous = false);
void
SetCallback (WatchpointHitCallback callback,
const lldb::BatonSP &callback_baton_sp,
bool is_synchronous = false);
void ClearCallback();
//------------------------------------------------------------------
/// Invoke the callback action when the watchpoint is hit.
///
@@ -78,10 +114,10 @@ public:
// Condition
//------------------------------------------------------------------
//------------------------------------------------------------------
/// Set the breakpoint's condition.
/// Set the watchpoint's condition.
///
/// @param[in] condition
/// The condition expression to evaluate when the breakpoint is hit.
/// The condition expression to evaluate when the watchpoint is hit.
/// Pass in NULL to clear the condition.
//------------------------------------------------------------------
void SetCondition (const char *condition);
@@ -111,11 +147,11 @@ private:
m_watch_was_read:1, // Set to 1 when watchpoint is hit for a read access
m_watch_was_written:1; // Set to 1 when watchpoint is hit for a write access
uint32_t m_ignore_count; // Number of times to ignore this breakpoint
WatchpointHitCallback m_callback;
void * m_callback_baton; // Callback user data to pass to callback
std::string m_decl_str; // Declaration information, if any.
std::string m_watch_spec_str; // Spec for the watchpoint (for future use).
Error m_error; // An error object describing errors associated with this watchpoint.
WatchpointOptions m_options; // Settable watchpoint options, which is a delegate to handle
// the callback machinery.
std::auto_ptr<ClangUserExpression> m_condition_ap; // The condition to test.

View File

@@ -0,0 +1,256 @@
//===-- WatchpointOptions.h -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_WatchpointOptions_h_
#define liblldb_WatchpointOptions_h_
// C Includes
// C++ Includes
#include <memory>
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Core/Baton.h"
#include "lldb/Core/StringList.h"
namespace lldb_private {
//----------------------------------------------------------------------
/// @class WatchpointOptions WatchpointOptions.h "lldb/Breakpoint/WatchpointOptions.h"
/// @brief Class that manages the options on a watchpoint.
//----------------------------------------------------------------------
class WatchpointOptions
{
public:
//------------------------------------------------------------------
// Constructors and Destructors
//------------------------------------------------------------------
//------------------------------------------------------------------
/// Default constructor. The watchpoint is enabled, and has no condition,
/// callback, ignore count, etc...
//------------------------------------------------------------------
WatchpointOptions();
WatchpointOptions(const WatchpointOptions& rhs);
static WatchpointOptions *
CopyOptionsNoCallback (WatchpointOptions &rhs);
//------------------------------------------------------------------
/// This constructor allows you to specify all the watchpoint options.
///
/// @param[in] callback
/// This is the plugin for some code that gets run, returns \b true if we are to stop.
///
/// @param[in] baton
/// Client data that will get passed to the callback.
///
/// @param[in] thread_id
/// Only stop if \a thread_id hits the watchpoint.
//------------------------------------------------------------------
WatchpointOptions(WatchpointHitCallback callback,
void *baton,
lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID);
virtual ~WatchpointOptions();
//------------------------------------------------------------------
// Operators
//------------------------------------------------------------------
const WatchpointOptions&
operator=(const WatchpointOptions& rhs);
//------------------------------------------------------------------
// Callbacks
//
// Watchpoint callbacks come in two forms, synchronous and asynchronous. Synchronous callbacks will get
// run before any of the thread plans are consulted, and if they return false the target will continue
// "under the radar" of the thread plans. There are a couple of restrictions to synchronous callbacks:
// 1) They should NOT resume the target themselves. Just return false if you want the target to restart.
// 2) Watchpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they
// won't do anything. Ditto with ignore counts, etc... You are supposed to control that all through the
// callback.
// Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is:
// a) If the watchpoint is thread specific and not for this thread, continue w/o running the callback.
// b) If the ignore count says we shouldn't stop, then ditto.
// c) If the condition says we shouldn't stop, then ditto.
// d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't.
// The asynchronous callback can run the target itself, but at present that should be the last action the
// callback does. We will relax this condition at some point, but it will take a bit of plumbing to get
// that to work.
//
//------------------------------------------------------------------
//------------------------------------------------------------------
/// Adds a callback to the watchpoint option set.
///
/// @param[in] callback
/// The function to be called when the watchpoint gets hit.
///
/// @param[in] baton_sp
/// A baton which will get passed back to the callback when it is invoked.
///
/// @param[in] synchronous
/// Whether this is a synchronous or asynchronous callback. See discussion above.
//------------------------------------------------------------------
void SetCallback (WatchpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false);
//------------------------------------------------------------------
/// Remove the callback from this option set.
//------------------------------------------------------------------
void ClearCallback ();
// The rest of these functions are meant to be used only within the watchpoint handling mechanism.
//------------------------------------------------------------------
/// Use this function to invoke the callback for a specific stop.
///
/// @param[in] context
/// The context in which the callback is to be invoked. This includes the stop event, the
/// execution context of the stop (since you might hit the same watchpoint on multiple threads) and
/// whether we are currently executing synchronous or asynchronous callbacks.
///
/// @param[in] watch_id
/// The watchpoint ID that owns this option set.
///
/// @return
/// The callback return value.
//------------------------------------------------------------------
bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t watch_id);
//------------------------------------------------------------------
/// Used in InvokeCallback to tell whether it is the right time to run this kind of callback.
///
/// @return
/// The synchronicity of our callback.
//------------------------------------------------------------------
bool IsCallbackSynchronous () {
return m_callback_is_synchronous;
}
//------------------------------------------------------------------
/// Fetch the baton from the callback.
///
/// @return
/// The baton.
//------------------------------------------------------------------
Baton *GetBaton ();
//------------------------------------------------------------------
/// Fetch a const version of the baton from the callback.
///
/// @return
/// The baton.
//------------------------------------------------------------------
const Baton *GetBaton () const;
//------------------------------------------------------------------
/// Return the current thread spec for this option. This will return NULL if the no thread
/// specifications have been set for this Option yet.
/// @return
/// The thread specification pointer for this option, or NULL if none has
/// been set yet.
//------------------------------------------------------------------
const ThreadSpec *
GetThreadSpecNoCreate () const;
//------------------------------------------------------------------
/// Returns a pointer to the ThreadSpec for this option, creating it.
/// if it hasn't been created already. This API is used for setting the
/// ThreadSpec items for this option.
//------------------------------------------------------------------
ThreadSpec *
GetThreadSpec ();
void
SetThreadID(lldb::tid_t thread_id);
void
GetDescription (Stream *s, lldb::DescriptionLevel level) const;
//------------------------------------------------------------------
/// Get description for callback only.
//------------------------------------------------------------------
void
GetCallbackDescription (Stream *s, lldb::DescriptionLevel level) const;
//------------------------------------------------------------------
/// Returns true if the watchpoint option has a callback set.
//------------------------------------------------------------------
bool
HasCallback();
//------------------------------------------------------------------
/// This is the default empty callback.
/// @return
/// The thread id for which the watchpoint hit will stop,
/// LLDB_INVALID_THREAD_ID for all threads.
//------------------------------------------------------------------
static bool
NullCallback (void *baton,
StoppointCallbackContext *context,
lldb::user_id_t watch_id);
struct CommandData
{
CommandData () :
user_source(),
script_source(),
stop_on_error(true)
{
}
~CommandData ()
{
}
StringList user_source;
std::string script_source;
bool stop_on_error;
};
class CommandBaton : public Baton
{
public:
CommandBaton (CommandData *data) :
Baton (data)
{
}
virtual
~CommandBaton ()
{
delete ((CommandData *)m_data);
m_data = NULL;
}
virtual void
GetDescription (Stream *s, lldb::DescriptionLevel level) const;
};
protected:
//------------------------------------------------------------------
// Classes that inherit from WatchpointOptions can see and modify these
//------------------------------------------------------------------
private:
//------------------------------------------------------------------
// For WatchpointOptions only
//------------------------------------------------------------------
WatchpointHitCallback m_callback; // This is the callback function pointer
lldb::BatonSP m_callback_baton_sp; // This is the client data for the callback
bool m_callback_is_synchronous;
std::auto_ptr<ThreadSpec> m_thread_spec_ap; // Thread for which this watchpoint will take
};
} // namespace lldb_private
#endif // liblldb_WatchpointOptions_h_

View File

@@ -68,6 +68,11 @@ public:
const lldb::StackFrameSP& frame_sp,
const lldb::BreakpointLocationSP &bp_loc_sp);
typedef bool (*SWIGWatchpointCallbackFunction) (const char *python_function_name,
const char *session_dictionary_name,
const lldb::StackFrameSP& frame_sp,
const lldb::WatchpointSP &wp_sp);
typedef bool (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name,
void *session_dictionary,
const lldb::ValueObjectSP& valobj_sp,
@@ -147,6 +152,12 @@ public:
return false;
}
virtual bool
GenerateWatchpointCommandCallbackData (StringList &input, std::string& output)
{
return false;
}
virtual bool
GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token = NULL)
{
@@ -194,6 +205,10 @@ public:
CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options,
CommandReturnObject &result);
virtual void
CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
CommandReturnObject &result);
/// Set a one-liner as the callback for the breakpoint.
virtual void
SetBreakpointCommandCallback (BreakpointOptions *bp_options,
@@ -202,6 +217,14 @@ public:
return;
}
/// Set a one-liner as the callback for the watchpoint.
virtual void
SetWatchpointCommandCallback (WatchpointOptions *wp_options,
const char *oneliner)
{
return;
}
virtual bool
GetScriptedSummary (const char *function_name,
lldb::ValueObjectSP valobj,

View File

@@ -101,6 +101,9 @@ public:
bool
GenerateBreakpointCommandCallbackData (StringList &input, std::string& output);
bool
GenerateWatchpointCommandCallbackData (StringList &input, std::string& output);
static size_t
GenerateBreakpointOptionsCommandCallback (void *baton,
InputReader &reader,
@@ -108,12 +111,24 @@ public:
const char *bytes,
size_t bytes_len);
static size_t
GenerateWatchpointOptionsCommandCallback (void *baton,
InputReader &reader,
lldb::InputReaderAction notification,
const char *bytes,
size_t bytes_len);
static bool
BreakpointCallbackFunction (void *baton,
StoppointCallbackContext *context,
lldb::user_id_t break_id,
lldb::user_id_t break_loc_id);
static bool
WatchpointCallbackFunction (void *baton,
StoppointCallbackContext *context,
lldb::user_id_t watch_id);
virtual bool
GetScriptedSummary (const char *function_name,
lldb::ValueObjectSP valobj,
@@ -135,11 +150,20 @@ public:
CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options,
CommandReturnObject &result);
void
CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
CommandReturnObject &result);
/// Set a Python one-liner as the callback for the breakpoint.
void
SetBreakpointCommandCallback (BreakpointOptions *bp_options,
const char *oneliner);
/// Set a one-liner as the callback for the watchpoint.
void
SetWatchpointCommandCallback (WatchpointOptions *wp_options,
const char *oneliner);
StringList
ReadCommandInputFromUser (FILE *in_file);

View File

@@ -235,6 +235,7 @@ class Variable;
class VariableList;
class Watchpoint;
class WatchpointList;
class WatchpointOptions;
struct LineEntry;
} // namespace lldb_private

View File

@@ -31,7 +31,7 @@ namespace lldb_private
typedef SymbolFile* (*SymbolFileCreateInstance) (ObjectFile* obj_file);
typedef SymbolVendor* (*SymbolVendorCreateInstance) (const lldb::ModuleSP &module_sp); // Module can be NULL for default system symbol vendor
typedef bool (*BreakpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
typedef bool (*WatchpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id, uint32_t type);
typedef bool (*WatchpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id);
typedef ThreadPlan * (*ThreadPlanShouldStopHereCallback) (ThreadPlan *current_plan, Flags &flags, void *baton);
typedef UnwindAssembly* (*UnwindAssemblyCreateInstance) (const ArchSpec &arch);
typedef int (*ComparisonFunction)(const void *, const void *);

View File

@@ -520,6 +520,8 @@
B299580B14F2FA1400050A04 /* DisassemblerLLVMC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B299580A14F2FA1400050A04 /* DisassemblerLLVMC.cpp */; };
B2A58722143119810092BFBA /* SBWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = B2A58721143119810092BFBA /* SBWatchpoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
B2A58724143119D50092BFBA /* SBWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2A58723143119D50092BFBA /* SBWatchpoint.cpp */; };
B2B7CCEB15D1BD6700EEFB57 /* CommandObjectWatchpointCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */; };
B2B7CCF015D1C20F00EEFB57 /* WatchpointOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */; };
ED88244E15114A9200BC98B9 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB919B414F6F10D008FF64B /* Security.framework */; };
ED88245015114CA200BC98B9 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED88244F15114CA200BC98B9 /* main.mm */; };
ED88245115114CA200BC98B9 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED88244F15114CA200BC98B9 /* main.mm */; };
@@ -1557,6 +1559,10 @@
B2A58721143119810092BFBA /* SBWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBWatchpoint.h; path = include/lldb/API/SBWatchpoint.h; sourceTree = "<group>"; };
B2A58723143119D50092BFBA /* SBWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBWatchpoint.cpp; path = source/API/SBWatchpoint.cpp; sourceTree = "<group>"; };
B2A5872514313B480092BFBA /* SBWatchpoint.i */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = SBWatchpoint.i; sourceTree = "<group>"; };
B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectWatchpointCommand.cpp; path = source/Commands/CommandObjectWatchpointCommand.cpp; sourceTree = "<group>"; };
B2B7CCEC15D1BD9600EEFB57 /* CommandObjectWatchpointCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandObjectWatchpointCommand.h; path = source/Commands/CommandObjectWatchpointCommand.h; sourceTree = "<group>"; };
B2B7CCED15D1BFB700EEFB57 /* WatchpointOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WatchpointOptions.h; path = include/lldb/Breakpoint/WatchpointOptions.h; sourceTree = "<group>"; };
B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WatchpointOptions.cpp; path = source/Breakpoint/WatchpointOptions.cpp; sourceTree = "<group>"; };
B2D3033612EFA5C500F84EB3 /* InstructionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InstructionUtils.h; path = Utility/InstructionUtils.h; sourceTree = "<group>"; };
ED88244F15114CA200BC98B9 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
ED88245215114CFC00BC98B9 /* LauncherRootXPCService.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LauncherRootXPCService.mm; sourceTree = "<group>"; };
@@ -2599,6 +2605,8 @@
B27318431416AC43006039C8 /* WatchpointList.h */,
26BC7E1810F1B83100F91463 /* Watchpoint.cpp */,
B27318411416AC12006039C8 /* WatchpointList.cpp */,
B2B7CCED15D1BFB700EEFB57 /* WatchpointOptions.h */,
B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */,
);
name = Breakpoint;
sourceTree = "<group>";
@@ -2654,6 +2662,8 @@
B296983412C2FB2B002D92C3 /* CommandObjectVersion.cpp */,
B207C4941429609C00F36E4E /* CommandObjectWatchpoint.h */,
B207C4921429607D00F36E4E /* CommandObjectWatchpoint.cpp */,
B2B7CCEC15D1BD9600EEFB57 /* CommandObjectWatchpointCommand.h */,
B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */,
);
name = Commands;
sourceTree = "<group>";
@@ -3928,6 +3938,8 @@
2694E9A414FC0BBD0076DE67 /* PlatformLinux.cpp in Sources */,
26B1EFAE154638AF00E2DAC7 /* DWARFDeclContext.cpp in Sources */,
B21EB71515CC99F100E60059 /* cxa_demangle.cpp in Sources */,
B2B7CCEB15D1BD6700EEFB57 /* CommandObjectWatchpointCommand.cpp in Sources */,
B2B7CCF015D1C20F00EEFB57 /* WatchpointOptions.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -176,6 +176,85 @@ LLDBSwigPythonBreakpointCallbackFunction
return stop_at_breakpoint;
}
// This function is called by lldb_private::ScriptInterpreterPython::WatchpointCallbackFunction(...)
// and is used when a script command is attached to a watchpoint for execution.
SWIGEXPORT bool
LLDBSwigPythonWatchpointCallbackFunction
(
const char *python_function_name,
const char *session_dictionary_name,
const lldb::StackFrameSP& frame_sp,
const lldb::WatchpointSP& wp_sp
)
{
lldb::SBFrame sb_frame (frame_sp);
lldb::SBWatchpoint sb_wp(wp_sp);
bool stop_at_watchpoint = true;
PyObject *Frame_PyObj = SWIG_NewPointerObj((void *) &sb_frame, SWIGTYPE_p_lldb__SBFrame, 0);
PyObject *Wp_PyObj = SWIG_NewPointerObj ((void *) &sb_wp, SWIGTYPE_p_lldb__SBWatchpoint, 0);
if (Frame_PyObj == NULL || Wp_PyObj == NULL)
return stop_at_watchpoint;
if (!python_function_name || !session_dictionary_name)
return stop_at_watchpoint;
PyObject *session_dict, *pfunc;
PyObject *pargs, *pvalue;
session_dict = FindSessionDictionary (session_dictionary_name);
if (session_dict != NULL)
{
pfunc = ResolvePythonName (python_function_name, session_dict);
if (pfunc != NULL)
{
// Set up the arguments and call the function.
if (PyCallable_Check (pfunc))
{
pargs = PyTuple_New (3);
if (pargs == NULL)
{
if (PyErr_Occurred())
PyErr_Clear();
return stop_at_watchpoint;
}
PyTuple_SetItem (pargs, 0, Frame_PyObj); // This "steals" a reference to Frame_PyObj
PyTuple_SetItem (pargs, 1, Wp_PyObj); // This "steals" a reference to Wp_PyObj
PyTuple_SetItem (pargs, 2, session_dict); // This "steals" a reference to session_dict
pvalue = PyObject_CallObject (pfunc, pargs);
Py_DECREF (pargs);
if (pvalue != NULL)
{
Py_DECREF (pvalue);
}
else if (PyErr_Occurred ())
{
PyErr_Clear();
}
Py_INCREF (session_dict);
}
else if (PyErr_Occurred())
{
PyErr_Clear();
}
}
else if (PyErr_Occurred())
{
PyErr_Clear();
}
}
else if (PyErr_Occurred ())
{
PyErr_Clear ();
}
return stop_at_watchpoint;
}
SWIGEXPORT bool
LLDBSwigPythonCallTypeScript
(

View File

@@ -33,11 +33,10 @@ Watchpoint::Watchpoint (lldb::addr_t addr, size_t size, bool hardware) :
m_watch_was_read(0),
m_watch_was_written(0),
m_ignore_count(0),
m_callback(NULL),
m_callback_baton(NULL),
m_decl_str(),
m_watch_spec_str(),
m_error()
m_error(),
m_options ()
{
}
@@ -45,12 +44,29 @@ Watchpoint::~Watchpoint()
{
}
bool
Watchpoint::SetCallback (WatchpointHitCallback callback, void *callback_baton)
// This function is used when "baton" doesn't need to be freed
void
Watchpoint::SetCallback (WatchpointHitCallback callback, void *baton, bool is_synchronous)
{
m_callback = callback;
m_callback_baton = callback_baton;
return true;
// The default "Baton" class will keep a copy of "baton" and won't free
// or delete it when it goes goes out of scope.
m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous);
//SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged);
}
// This function is used when a baton needs to be freed and therefore is
// contained in a "Baton" subclass.
void
Watchpoint::SetCallback (WatchpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous)
{
m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
}
void
Watchpoint::ClearCallback ()
{
m_options.ClearCallback ();
}
void
@@ -129,26 +145,15 @@ Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) c
s->Printf("\n static watchpoint spec = '%s'", m_watch_spec_str.c_str());
if (GetConditionText())
s->Printf("\n condition = '%s'", GetConditionText());
m_options.GetCallbackDescription(s, description_level);
}
if (description_level >= lldb::eDescriptionLevelVerbose)
{
if (m_callback)
{
s->Printf("\n hw_index = %i hit_count = %-4u ignore_count = %-4u callback = %8p baton = %8p",
GetHardwareIndex(),
GetHitCount(),
GetIgnoreCount(),
m_callback,
m_callback_baton);
}
else
{
s->Printf("\n hw_index = %i hit_count = %-4u ignore_count = %-4u",
GetHardwareIndex(),
GetHitCount(),
GetIgnoreCount());
}
s->Printf("\n hw_index = %i hit_count = %-4u ignore_count = %-4u",
GetHardwareIndex(),
GetHitCount(),
GetIgnoreCount());
}
}
@@ -198,17 +203,7 @@ Watchpoint::SetIgnoreCount (uint32_t n)
bool
Watchpoint::InvokeCallback (StoppointCallbackContext *context)
{
if (m_callback && context->is_synchronous)
{
uint32_t access = 0;
if (m_watch_was_read)
access |= LLDB_WATCH_TYPE_READ;
if (m_watch_was_written)
access |= LLDB_WATCH_TYPE_WRITE;
return m_callback(m_callback_baton, context, GetID(), access);
}
else
return true;
return m_options.InvokeCallback (context, GetID());
}
void

View File

@@ -0,0 +1,244 @@
//===-- WatchpointOptions.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/Breakpoint/WatchpointOptions.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Stream.h"
#include "lldb/Core/StringList.h"
#include "lldb/Core/Value.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadSpec.h"
#include "lldb/Expression/ClangUserExpression.h"
using namespace lldb;
using namespace lldb_private;
bool
WatchpointOptions::NullCallback (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id)
{
return true;
}
//----------------------------------------------------------------------
// WatchpointOptions constructor
//----------------------------------------------------------------------
WatchpointOptions::WatchpointOptions() :
m_callback (WatchpointOptions::NullCallback),
m_callback_baton_sp (),
m_callback_is_synchronous (false),
m_thread_spec_ap (NULL)
{
}
//----------------------------------------------------------------------
// WatchpointOptions copy constructor
//----------------------------------------------------------------------
WatchpointOptions::WatchpointOptions(const WatchpointOptions& rhs) :
m_callback (rhs.m_callback),
m_callback_baton_sp (rhs.m_callback_baton_sp),
m_callback_is_synchronous (rhs.m_callback_is_synchronous),
m_thread_spec_ap (NULL)
{
if (rhs.m_thread_spec_ap.get() != NULL)
m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get()));
}
//----------------------------------------------------------------------
// WatchpointOptions assignment operator
//----------------------------------------------------------------------
const WatchpointOptions&
WatchpointOptions::operator=(const WatchpointOptions& rhs)
{
m_callback = rhs.m_callback;
m_callback_baton_sp = rhs.m_callback_baton_sp;
m_callback_is_synchronous = rhs.m_callback_is_synchronous;
if (rhs.m_thread_spec_ap.get() != NULL)
m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
return *this;
}
WatchpointOptions *
WatchpointOptions::CopyOptionsNoCallback (WatchpointOptions &orig)
{
WatchpointHitCallback orig_callback = orig.m_callback;
lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp;
bool orig_is_sync = orig.m_callback_is_synchronous;
orig.ClearCallback();
WatchpointOptions *ret_val = new WatchpointOptions(orig);
orig.SetCallback (orig_callback, orig_callback_baton_sp, orig_is_sync);
return ret_val;
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
WatchpointOptions::~WatchpointOptions()
{
}
//------------------------------------------------------------------
// Callbacks
//------------------------------------------------------------------
void
WatchpointOptions::SetCallback (WatchpointHitCallback callback, const BatonSP &callback_baton_sp, bool callback_is_synchronous)
{
m_callback_is_synchronous = callback_is_synchronous;
m_callback = callback;
m_callback_baton_sp = callback_baton_sp;
}
void
WatchpointOptions::ClearCallback ()
{
m_callback = WatchpointOptions::NullCallback;
m_callback_is_synchronous = false;
m_callback_baton_sp.reset();
}
Baton *
WatchpointOptions::GetBaton ()
{
return m_callback_baton_sp.get();
}
const Baton *
WatchpointOptions::GetBaton () const
{
return m_callback_baton_sp.get();
}
bool
WatchpointOptions::InvokeCallback (StoppointCallbackContext *context,
lldb::user_id_t watch_id)
{
if (m_callback && context->is_synchronous == IsCallbackSynchronous())
{
return m_callback (m_callback_baton_sp ? m_callback_baton_sp->m_data : NULL,
context,
watch_id);
}
else
return true;
}
bool
WatchpointOptions::HasCallback ()
{
return m_callback != WatchpointOptions::NullCallback;
}
const ThreadSpec *
WatchpointOptions::GetThreadSpecNoCreate () const
{
return m_thread_spec_ap.get();
}
ThreadSpec *
WatchpointOptions::GetThreadSpec ()
{
if (m_thread_spec_ap.get() == NULL)
m_thread_spec_ap.reset (new ThreadSpec());
return m_thread_spec_ap.get();
}
void
WatchpointOptions::SetThreadID (lldb::tid_t thread_id)
{
GetThreadSpec()->SetTID(thread_id);
}
void
WatchpointOptions::GetCallbackDescription (Stream *s, lldb::DescriptionLevel level) const
{
if (m_callback_baton_sp.get())
{
if (level != eDescriptionLevelBrief)
{
s->EOL();
m_callback_baton_sp->GetDescription (s, level);
}
}
}
void
WatchpointOptions::GetDescription (Stream *s, lldb::DescriptionLevel level) const
{
// Figure out if there are any options not at their default value, and only print
// anything if there are:
if ((GetThreadSpecNoCreate() != NULL && GetThreadSpecNoCreate()->HasSpecification ()))
{
if (level == lldb::eDescriptionLevelVerbose)
{
s->EOL ();
s->IndentMore();
s->Indent();
s->PutCString("Watchpoint Options:\n");
s->IndentMore();
s->Indent();
}
else
s->PutCString(" Options: ");
if (m_thread_spec_ap.get())
m_thread_spec_ap->GetDescription (s, level);
else if (level == eDescriptionLevelBrief)
s->PutCString ("thread spec: no ");
if (level == lldb::eDescriptionLevelFull)
{
s->IndentLess();
s->IndentMore();
}
}
GetCallbackDescription(s, level);
}
void
WatchpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLevel level) const
{
CommandData *data = (CommandData *)m_data;
if (level == eDescriptionLevelBrief)
{
s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no");
return;
}
s->IndentMore ();
s->Indent("Watchpoint commands:\n");
s->IndentMore ();
if (data && data->user_source.GetSize() > 0)
{
const size_t num_strings = data->user_source.GetSize();
for (size_t i = 0; i < num_strings; ++i)
{
s->Indent(data->user_source.GetStringAtIndex(i));
s->EOL();
}
}
else
{
s->PutCString ("No commands.\n");
}
s->IndentLess ();
s->IndentLess ();
}

View File

@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "CommandObjectWatchpoint.h"
#include "CommandObjectWatchpointCommand.h"
// C Includes
// C++ Includes
@@ -1300,6 +1301,7 @@ CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterp
CommandObjectSP disable_command_object (new CommandObjectWatchpointDisable (interpreter));
CommandObjectSP delete_command_object (new CommandObjectWatchpointDelete (interpreter));
CommandObjectSP ignore_command_object (new CommandObjectWatchpointIgnore (interpreter));
CommandObjectSP command_command_object (new CommandObjectWatchpointCommand (interpreter));
CommandObjectSP modify_command_object (new CommandObjectWatchpointModify (interpreter));
CommandObjectSP set_command_object (new CommandObjectWatchpointSet (interpreter));
@@ -1308,6 +1310,7 @@ CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterp
disable_command_object->SetCommandName("watchpoint disable");
delete_command_object->SetCommandName("watchpoint delete");
ignore_command_object->SetCommandName("watchpoint ignore");
command_command_object->SetCommandName ("watchpoint command");
modify_command_object->SetCommandName("watchpoint modify");
set_command_object->SetCommandName("watchpoint set");
@@ -1316,6 +1319,7 @@ CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterp
LoadSubCommand ("disable", disable_command_object);
LoadSubCommand ("delete", delete_command_object);
LoadSubCommand ("ignore", ignore_command_object);
LoadSubCommand ("command", command_command_object);
LoadSubCommand ("modify", modify_command_object);
LoadSubCommand ("set", set_command_object);
}

View File

@@ -0,0 +1,849 @@
//===-- CommandObjectWatchpointCommand.cpp ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
// C++ Includes
#include "CommandObjectWatchpointCommand.h"
#include "CommandObjectWatchpoint.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/State.h"
#include <vector>
using namespace lldb;
using namespace lldb_private;
//-------------------------------------------------------------------------
// CommandObjectWatchpointCommandAdd
//-------------------------------------------------------------------------
class CommandObjectWatchpointCommandAdd : public CommandObjectParsed
{
public:
CommandObjectWatchpointCommandAdd (CommandInterpreter &interpreter) :
CommandObjectParsed (interpreter,
"add",
"Add a set of commands to a watchpoint, to be executed whenever the watchpoint is hit.",
NULL),
m_options (interpreter)
{
SetHelpLong (
"\nGeneral information about entering watchpoint commands \n\
------------------------------------------------------ \n\
\n\
This command will cause you to be prompted to enter the command or set \n\
of commands you wish to be executed when the specified watchpoint is \n\
hit. You will be told to enter your command(s), and will see a '> ' \n\
prompt. Because you can enter one or many commands to be executed when \n\
a watchpoint is hit, you will continue to be prompted after each \n\
new-line that you enter, until you enter the word 'DONE', which will \n\
cause the commands you have entered to be stored with the watchpoint \n\
and executed when the watchpoint is hit. \n\
\n\
Syntax checking is not necessarily done when watchpoint commands are \n\
entered. An improperly written watchpoint command will attempt to get \n\
executed when the watchpoint gets hit, and usually silently fail. If \n\
your watchpoint command does not appear to be getting executed, go \n\
back and check your syntax. \n\
\n\
\n\
Special information about PYTHON watchpoint commands \n\
---------------------------------------------------- \n\
\n\
You may enter either one line of Python or multiple lines of Python \n\
(including defining whole functions, if desired). If you enter a \n\
single line of Python, that will be passed to the Python interpreter \n\
'as is' when the watchpoint gets hit. If you enter function \n\
definitions, they will be passed to the Python interpreter as soon as \n\
you finish entering the watchpoint command, and they can be called \n\
later (don't forget to add calls to them, if you want them called when \n\
the watchpoint is hit). If you enter multiple lines of Python that \n\
are not function definitions, they will be collected into a new, \n\
automatically generated Python function, and a call to the newly \n\
generated function will be attached to the watchpoint. \n\
\n\
This auto-generated function is passed in two arguments: \n\
\n\
frame: an SBFrame object representing the frame which hit the watchpoint. \n\
From the frame you can get back to the thread and process. \n\
wp: the watchpoint that was hit. \n\
\n\
Important Note: Because loose Python code gets collected into functions, \n\
if you want to access global variables in the 'loose' code, you need to \n\
specify that they are global, using the 'global' keyword. Be sure to \n\
use correct Python syntax, including indentation, when entering Python \n\
watchpoint commands. \n\
\n\
As a third option, you can pass the name of an already existing Python function \n\
and that function will be attached to the watchpoint. It will get passed the \n\
frame and wp_loc arguments mentioned above. \n\
\n\
Example Python one-line watchpoint command: \n\
\n\
(lldb) watchpoint command add -s python 1 \n\
Enter your Python command(s). Type 'DONE' to end. \n\
> print \"Hit this watchpoint!\" \n\
> DONE \n\
\n\
As a convenience, this also works for a short Python one-liner: \n\
(lldb) watchpoint command add -s python 1 -o \"import time; print time.asctime()\" \n\
(lldb) run \n\
Launching '.../a.out' (x86_64) \n\
(lldb) Fri Sep 10 12:17:45 2010 \n\
Process 21778 Stopped \n\
* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread \n\
36 \n\
37 int c(int val)\n\
38 {\n\
39 -> return val + 3;\n\
40 }\n\
41 \n\
42 int main (int argc, char const *argv[])\n\
(lldb) \n\
\n\
Example multiple line Python watchpoint command, using function definition: \n\
\n\
(lldb) watchpoint command add -s python 1 \n\
Enter your Python command(s). Type 'DONE' to end. \n\
> def watchpoint_output (wp_no): \n\
> out_string = \"Hit watchpoint number \" + repr (wp_no) \n\
> print out_string \n\
> return True \n\
> watchpoint_output (1) \n\
> DONE \n\
\n\
\n\
Example multiple line Python watchpoint command, using 'loose' Python: \n\
\n\
(lldb) watchpoint command add -s p 1 \n\
Enter your Python command(s). Type 'DONE' to end. \n\
> global wp_count \n\
> wp_count = wp_count + 1 \n\
> print \"Hit this watchpoint \" + repr(wp_count) + \" times!\" \n\
> DONE \n\
\n\
In this case, since there is a reference to a global variable, \n\
'wp_count', you will also need to make sure 'wp_count' exists and is \n\
initialized: \n\
\n\
(lldb) script \n\
>>> wp_count = 0 \n\
>>> quit() \n\
\n\
(lldb) \n\
\n\
\n\
Final Note: If you get a warning that no watchpoint command was generated, \n\
but you did not get any syntax errors, you probably forgot to add a call \n\
to your functions. \n\
\n\
Special information about debugger command watchpoint commands \n\
-------------------------------------------------------------- \n\
\n\
You may enter any debugger command, exactly as you would at the \n\
debugger prompt. You may enter as many debugger commands as you like, \n\
but do NOT enter more than one command per line. \n" );
CommandArgumentEntry arg;
CommandArgumentData wp_id_arg;
// Define the first (and only) variant of this arg.
wp_id_arg.arg_type = eArgTypeWatchpointID;
wp_id_arg.arg_repetition = eArgRepeatPlain;
// There is only one variant this argument could be; put it into the argument entry.
arg.push_back (wp_id_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back (arg);
}
virtual
~CommandObjectWatchpointCommandAdd () {}
virtual Options *
GetOptions ()
{
return &m_options;
}
void
CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
CommandReturnObject &result)
{
InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger()));
std::auto_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
if (reader_sp && data_ap.get())
{
BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp);
Error err (reader_sp->Initialize (CommandObjectWatchpointCommandAdd::GenerateWatchpointCommandCallback,
wp_options, // callback_data
eInputReaderGranularityLine, // token size, to pass to callback function
"DONE", // end token
"> ", // prompt
true)); // echo input
if (err.Success())
{
m_interpreter.GetDebugger().PushInputReader (reader_sp);
result.SetStatus (eReturnStatusSuccessFinishNoResult);
}
else
{
result.AppendError (err.AsCString());
result.SetStatus (eReturnStatusFailed);
}
}
else
{
result.AppendError("out of memory");
result.SetStatus (eReturnStatusFailed);
}
}
/// Set a one-liner as the callback for the watchpoint.
void
SetWatchpointCommandCallback (WatchpointOptions *wp_options,
const char *oneliner)
{
std::auto_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
// It's necessary to set both user_source and script_source to the oneliner.
// The former is used to generate callback description (as in watchpoint command list)
// while the latter is used for Python to interpret during the actual callback.
data_ap->user_source.AppendString (oneliner);
data_ap->script_source.assign (oneliner);
data_ap->stop_on_error = m_options.m_stop_on_error;
BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp);
return;
}
static size_t
GenerateWatchpointCommandCallback (void *callback_data,
InputReader &reader,
lldb::InputReaderAction notification,
const char *bytes,
size_t bytes_len)
{
StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream();
bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
switch (notification)
{
case eInputReaderActivate:
if (!batch_mode)
{
out_stream->Printf ("%s\n", g_reader_instructions);
if (reader.GetPrompt())
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush();
}
break;
case eInputReaderDeactivate:
break;
case eInputReaderReactivate:
if (reader.GetPrompt() && !batch_mode)
{
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush();
}
break;
case eInputReaderAsynchronousOutputWritten:
break;
case eInputReaderGotToken:
if (bytes && bytes_len && callback_data)
{
WatchpointOptions *wp_options = (WatchpointOptions *) callback_data;
if (wp_options)
{
Baton *wp_options_baton = wp_options->GetBaton();
if (wp_options_baton)
((WatchpointOptions::CommandData *)wp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len);
}
}
if (!reader.IsDone() && reader.GetPrompt() && !batch_mode)
{
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush();
}
break;
case eInputReaderInterrupt:
{
// Finish, and cancel the watchpoint command.
reader.SetIsDone (true);
WatchpointOptions *wp_options = (WatchpointOptions *) callback_data;
if (wp_options)
{
Baton *wp_options_baton = wp_options->GetBaton ();
if (wp_options_baton)
{
((WatchpointOptions::CommandData *) wp_options_baton->m_data)->user_source.Clear();
((WatchpointOptions::CommandData *) wp_options_baton->m_data)->script_source.clear();
}
}
if (!batch_mode)
{
out_stream->Printf ("Warning: No command attached to watchpoint.\n");
out_stream->Flush();
}
}
break;
case eInputReaderEndOfFile:
reader.SetIsDone (true);
break;
case eInputReaderDone:
break;
}
return bytes_len;
}
static bool
WatchpointOptionsCallbackFunction (void *baton,
StoppointCallbackContext *context,
lldb::user_id_t watch_id)
{
bool ret_value = true;
if (baton == NULL)
return true;
WatchpointOptions::CommandData *data = (WatchpointOptions::CommandData *) baton;
StringList &commands = data->user_source;
if (commands.GetSize() > 0)
{
ExecutionContext exe_ctx (context->exe_ctx_ref);
Target *target = exe_ctx.GetTargetPtr();
if (target)
{
CommandReturnObject result;
Debugger &debugger = target->GetDebugger();
// Rig up the results secondary output stream to the debugger's, so the output will come out synchronously
// if the debugger is set up that way.
StreamSP output_stream (debugger.GetAsyncOutputStream());
StreamSP error_stream (debugger.GetAsyncErrorStream());
result.SetImmediateOutputStream (output_stream);
result.SetImmediateErrorStream (error_stream);
bool stop_on_continue = true;
bool echo_commands = false;
bool print_results = true;
debugger.GetCommandInterpreter().HandleCommands (commands,
&exe_ctx,
stop_on_continue,
data->stop_on_error,
echo_commands,
print_results,
eLazyBoolNo,
result);
result.GetImmediateOutputStream()->Flush();
result.GetImmediateErrorStream()->Flush();
}
}
return ret_value;
}
class CommandOptions : public Options
{
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter),
m_use_commands (false),
m_use_script_language (false),
m_script_language (eScriptLanguageNone),
m_use_one_liner (false),
m_one_liner(),
m_function_name()
{
}
virtual
~CommandOptions () {}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
switch (short_option)
{
case 'o':
m_use_one_liner = true;
m_one_liner = option_arg;
break;
case 's':
m_script_language = (lldb::ScriptLanguage) Args::StringToOptionEnum (option_arg,
g_option_table[option_idx].enum_values,
eScriptLanguageNone,
error);
if (m_script_language == eScriptLanguagePython || m_script_language == eScriptLanguageDefault)
{
m_use_script_language = true;
}
else
{
m_use_script_language = false;
}
break;
case 'e':
{
bool success = false;
m_stop_on_error = Args::StringToBoolean(option_arg, false, &success);
if (!success)
error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg);
}
break;
case 'F':
{
m_use_one_liner = false;
m_use_script_language = true;
m_function_name.assign(option_arg);
}
break;
default:
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_use_commands = true;
m_use_script_language = false;
m_script_language = eScriptLanguageNone;
m_use_one_liner = false;
m_stop_on_error = true;
m_one_liner.clear();
m_function_name.clear();
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
bool m_use_commands;
bool m_use_script_language;
lldb::ScriptLanguage m_script_language;
// Instance variables to hold the values for one_liner options.
bool m_use_one_liner;
std::string m_one_liner;
bool m_stop_on_error;
std::string m_function_name;
};
protected:
virtual bool
DoExecute (Args& command, CommandReturnObject &result)
{
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
if (target == NULL)
{
result.AppendError ("There is not a current executable; there are no watchpoints to which to add commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
const WatchpointList &watchpoints = target->GetWatchpointList();
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0)
{
result.AppendError ("No watchpoints exist to have commands added");
result.SetStatus (eReturnStatusFailed);
return false;
}
if (m_options.m_use_script_language == false && m_options.m_function_name.size())
{
result.AppendError ("need to enable scripting to have a function run as a watchpoint command");
result.SetStatus (eReturnStatusFailed);
return false;
}
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, valid_wp_ids))
{
result.AppendError("Invalid watchpoints specification.");
result.SetStatus(eReturnStatusFailed);
return false;
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
const size_t count = valid_wp_ids.size();
for (size_t i = 0; i < count; ++i)
{
uint32_t cur_wp_id = valid_wp_ids.at (i);
if (cur_wp_id != LLDB_INVALID_WATCH_ID)
{
Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
// Sanity check wp first.
if (wp == NULL) continue;
WatchpointOptions *wp_options = wp->GetOptions();
// Skip this watchpoint if wp_options is not good.
if (wp_options == NULL) continue;
// If we are using script language, get the script interpreter
// in order to set or collect command callback. Otherwise, call
// the methods associated with this object.
if (m_options.m_use_script_language)
{
// Special handling for one-liner specified inline.
if (m_options.m_use_one_liner)
{
m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options,
m_options.m_one_liner.c_str());
}
// Special handling for using a Python function by name
// instead of extending the watchpoint callback data structures, we just automatize
// what the user would do manually: make their watchpoint command be a function call
else if (m_options.m_function_name.size())
{
std::string oneliner(m_options.m_function_name);
oneliner += "(frame, wp, internal_dict)";
m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options,
oneliner.c_str());
}
else
{
m_interpreter.GetScriptInterpreter()->CollectDataForWatchpointCommandCallback (wp_options,
result);
}
}
else
{
// Special handling for one-liner specified inline.
if (m_options.m_use_one_liner)
SetWatchpointCommandCallback (wp_options,
m_options.m_one_liner.c_str());
else
CollectDataForWatchpointCommandCallback (wp_options,
result);
}
}
}
return result.Succeeded();
}
private:
CommandOptions m_options;
static const char *g_reader_instructions;
};
const char *
CommandObjectWatchpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end.";
// FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting
// language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper.
static OptionEnumValueElement
g_script_option_enumeration[4] =
{
{ eScriptLanguageNone, "command", "Commands are in the lldb command interpreter language"},
{ eScriptLanguagePython, "python", "Commands are in the Python language."},
{ eSortOrderByName, "default-script", "Commands are in the default scripting language."},
{ 0, NULL, NULL }
};
OptionDefinition
CommandObjectWatchpointCommandAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_1, false, "one-liner", 'o', required_argument, NULL, NULL, eArgTypeOneLiner,
"Specify a one-line watchpoint command inline. Be sure to surround it with quotes." },
{ LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, NULL, eArgTypeBoolean,
"Specify whether watchpoint command execution should terminate on error." },
{ LLDB_OPT_SET_ALL, false, "script-type", 's', required_argument, g_script_option_enumeration, NULL, eArgTypeNone,
"Specify the language for the commands - if none is specified, the lldb command interpreter will be used."},
{ LLDB_OPT_SET_2, false, "python-function", 'F', required_argument, NULL, NULL, eArgTypePythonFunction,
"Give the name of a Python function to run as command for this watchpoint. Be sure to give a module name if appropriate."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectWatchpointCommandDelete
//-------------------------------------------------------------------------
class CommandObjectWatchpointCommandDelete : public CommandObjectParsed
{
public:
CommandObjectWatchpointCommandDelete (CommandInterpreter &interpreter) :
CommandObjectParsed (interpreter,
"delete",
"Delete the set of commands from a watchpoint.",
NULL)
{
CommandArgumentEntry arg;
CommandArgumentData wp_id_arg;
// Define the first (and only) variant of this arg.
wp_id_arg.arg_type = eArgTypeWatchpointID;
wp_id_arg.arg_repetition = eArgRepeatPlain;
// There is only one variant this argument could be; put it into the argument entry.
arg.push_back (wp_id_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back (arg);
}
virtual
~CommandObjectWatchpointCommandDelete () {}
protected:
virtual bool
DoExecute (Args& command, CommandReturnObject &result)
{
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
if (target == NULL)
{
result.AppendError ("There is not a current executable; there are no watchpoints from which to delete commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
const WatchpointList &watchpoints = target->GetWatchpointList();
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0)
{
result.AppendError ("No watchpoints exist to have commands deleted");
result.SetStatus (eReturnStatusFailed);
return false;
}
if (command.GetArgumentCount() == 0)
{
result.AppendError ("No watchpoint specified from which to delete the commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, valid_wp_ids))
{
result.AppendError("Invalid watchpoints specification.");
result.SetStatus(eReturnStatusFailed);
return false;
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
const size_t count = valid_wp_ids.size();
for (size_t i = 0; i < count; ++i)
{
uint32_t cur_wp_id = valid_wp_ids.at (i);
if (cur_wp_id != LLDB_INVALID_WATCH_ID)
{
Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
if (wp)
wp->ClearCallback();
}
else
{
result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",
cur_wp_id);
result.SetStatus (eReturnStatusFailed);
return false;
}
}
return result.Succeeded();
}
};
//-------------------------------------------------------------------------
// CommandObjectWatchpointCommandList
//-------------------------------------------------------------------------
class CommandObjectWatchpointCommandList : public CommandObjectParsed
{
public:
CommandObjectWatchpointCommandList (CommandInterpreter &interpreter) :
CommandObjectParsed (interpreter,
"list",
"List the script or set of commands to be executed when the watchpoint is hit.",
NULL)
{
CommandArgumentEntry arg;
CommandArgumentData wp_id_arg;
// Define the first (and only) variant of this arg.
wp_id_arg.arg_type = eArgTypeWatchpointID;
wp_id_arg.arg_repetition = eArgRepeatPlain;
// There is only one variant this argument could be; put it into the argument entry.
arg.push_back (wp_id_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back (arg);
}
virtual
~CommandObjectWatchpointCommandList () {}
protected:
virtual bool
DoExecute (Args& command,
CommandReturnObject &result)
{
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
if (target == NULL)
{
result.AppendError ("There is not a current executable; there are no watchpoints for which to list commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
const WatchpointList &watchpoints = target->GetWatchpointList();
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0)
{
result.AppendError ("No watchpoints exist for which to list commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
if (command.GetArgumentCount() == 0)
{
result.AppendError ("No watchpoint specified for which to list the commands");
result.SetStatus (eReturnStatusFailed);
return false;
}
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, valid_wp_ids))
{
result.AppendError("Invalid watchpoints specification.");
result.SetStatus(eReturnStatusFailed);
return false;
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
const size_t count = valid_wp_ids.size();
for (size_t i = 0; i < count; ++i)
{
uint32_t cur_wp_id = valid_wp_ids.at (i);
if (cur_wp_id != LLDB_INVALID_WATCH_ID)
{
Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
if (wp)
{
const WatchpointOptions *wp_options = wp->GetOptions();
if (wp_options)
{
// Get the callback baton associated with the current watchpoint.
const Baton *baton = wp_options->GetBaton();
if (baton)
{
result.GetOutputStream().Printf ("Watchpoint %u:\n", cur_wp_id);
result.GetOutputStream().IndentMore ();
baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
result.GetOutputStream().IndentLess ();
}
else
{
result.AppendMessageWithFormat ("Watchpoint %u does not have an associated command.\n",
cur_wp_id);
}
}
result.SetStatus (eReturnStatusSuccessFinishResult);
}
else
{
result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);
result.SetStatus (eReturnStatusFailed);
}
}
}
return result.Succeeded();
}
};
//-------------------------------------------------------------------------
// CommandObjectWatchpointCommand
//-------------------------------------------------------------------------
CommandObjectWatchpointCommand::CommandObjectWatchpointCommand (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"command",
"A set of commands for adding, removing and examining bits of code to be executed when the watchpoint is hit (watchpoint 'commmands').",
"command <sub-command> [<sub-command-options>] <watchpoint-id>")
{
bool status;
CommandObjectSP add_command_object (new CommandObjectWatchpointCommandAdd (interpreter));
CommandObjectSP delete_command_object (new CommandObjectWatchpointCommandDelete (interpreter));
CommandObjectSP list_command_object (new CommandObjectWatchpointCommandList (interpreter));
add_command_object->SetCommandName ("watchpoint command add");
delete_command_object->SetCommandName ("watchpoint command delete");
list_command_object->SetCommandName ("watchpoint command list");
status = LoadSubCommand ("add", add_command_object);
status = LoadSubCommand ("delete", delete_command_object);
status = LoadSubCommand ("list", list_command_object);
}
CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand ()
{
}

View File

@@ -0,0 +1,46 @@
//===-- CommandObjectWatchpointCommand.h ------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_CommandObjectWatchpointCommand_h_
#define liblldb_CommandObjectWatchpointCommand_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-types.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Core/InputReader.h"
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
namespace lldb_private {
//-------------------------------------------------------------------------
// CommandObjectMultiwordWatchpoint
//-------------------------------------------------------------------------
class CommandObjectWatchpointCommand : public CommandObjectMultiword
{
public:
CommandObjectWatchpointCommand (CommandInterpreter &interpreter);
virtual
~CommandObjectWatchpointCommand ();
};
} // namespace lldb_private
#endif // liblldb_CommandObjectWatchpointCommand_h_

View File

@@ -50,6 +50,17 @@ ScriptInterpreter::CollectDataForBreakpointCommandCallback
result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented.");
}
void
ScriptInterpreter::CollectDataForWatchpointCommandCallback
(
WatchpointOptions *bp_options,
CommandReturnObject &result
)
{
result.SetStatus (eReturnStatusFailed);
result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented.");
}
std::string
ScriptInterpreter::LanguageToString (lldb::ScriptLanguage language)
{

View File

@@ -31,6 +31,7 @@
#include "lldb/API/SBValue.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Breakpoint/WatchpointOptions.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Timer.h"
#include "lldb/Host/Host.h"
@@ -44,6 +45,7 @@ using namespace lldb_private;
static ScriptInterpreter::SWIGInitCallback g_swig_init_callback = NULL;
static ScriptInterpreter::SWIGBreakpointCallbackFunction g_swig_breakpoint_callback = NULL;
static ScriptInterpreter::SWIGWatchpointCallbackFunction g_swig_watchpoint_callback = NULL;
static ScriptInterpreter::SWIGPythonTypeScriptCallbackFunction g_swig_typescript_callback = NULL;
static ScriptInterpreter::SWIGPythonCreateSyntheticProvider g_swig_synthetic_script = NULL;
static ScriptInterpreter::SWIGPythonCalculateNumChildren g_swig_calc_children = NULL;
@@ -68,6 +70,15 @@ LLDBSwigPythonBreakpointCallbackFunction
const lldb::BreakpointLocationSP& sb_bp_loc
);
extern "C" bool
LLDBSwigPythonWatchpointCallbackFunction
(
const char *python_function_name,
const char *session_dictionary_name,
const lldb::StackFrameSP& sb_frame,
const lldb::WatchpointSP& sb_wp
);
extern "C" bool
LLDBSwigPythonCallTypeScript
(
@@ -1388,6 +1399,112 @@ ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback
return bytes_len;
}
size_t
ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback
(
void *baton,
InputReader &reader,
InputReaderAction notification,
const char *bytes,
size_t bytes_len
)
{
static StringList commands_in_progress;
StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream();
bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
switch (notification)
{
case eInputReaderActivate:
{
commands_in_progress.Clear();
if (!batch_mode)
{
out_stream->Printf ("%s\n", g_reader_instructions);
if (reader.GetPrompt())
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush ();
}
}
break;
case eInputReaderDeactivate:
break;
case eInputReaderReactivate:
if (reader.GetPrompt() && !batch_mode)
{
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush ();
}
break;
case eInputReaderAsynchronousOutputWritten:
break;
case eInputReaderGotToken:
{
std::string temp_string (bytes, bytes_len);
commands_in_progress.AppendString (temp_string.c_str());
if (!reader.IsDone() && reader.GetPrompt() && !batch_mode)
{
out_stream->Printf ("%s", reader.GetPrompt());
out_stream->Flush ();
}
}
break;
case eInputReaderEndOfFile:
case eInputReaderInterrupt:
// Control-c (SIGINT) & control-d both mean finish & exit.
reader.SetIsDone(true);
// Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command.
if (notification == eInputReaderInterrupt)
commands_in_progress.Clear();
// Fall through here...
case eInputReaderDone:
{
WatchpointOptions *wp_options = (WatchpointOptions *)baton;
std::auto_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
data_ap->user_source.AppendList (commands_in_progress);
if (data_ap.get())
{
ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
if (interpreter)
{
if (interpreter->GenerateWatchpointCommandCallbackData (data_ap->user_source,
data_ap->script_source))
{
BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp);
}
else if (!batch_mode)
{
out_stream->Printf ("Warning: No command attached to breakpoint.\n");
out_stream->Flush();
}
}
else
{
if (!batch_mode)
{
out_stream->Printf ("Warning: Unable to find script intepreter; no command attached to breakpoint.\n");
out_stream->Flush();
}
}
}
}
break;
}
return bytes_len;
}
void
ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options,
CommandReturnObject &result)
@@ -1421,6 +1538,39 @@ ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOpti
}
}
void
ScriptInterpreterPython::CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
CommandReturnObject &result)
{
Debugger &debugger = GetCommandInterpreter().GetDebugger();
InputReaderSP reader_sp (new InputReader (debugger));
if (reader_sp)
{
Error err = reader_sp->Initialize (
ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback,
wp_options, // baton
eInputReaderGranularityLine, // token size, for feeding data to callback function
"DONE", // end token
"> ", // prompt
true); // echo input
if (err.Success())
debugger.PushInputReader (reader_sp);
else
{
result.AppendError (err.AsCString());
result.SetStatus (eReturnStatusFailed);
}
}
else
{
result.AppendError("out of memory");
result.SetStatus (eReturnStatusFailed);
}
}
// Set a Python one-liner as the callback for the breakpoint.
void
ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_options,
@@ -1433,6 +1583,7 @@ ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_opt
// while the latter is used for Python to interpret during the actual callback.
data_ap->user_source.AppendString (oneliner);
data_ap->script_source.assign (oneliner);
if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source))
{
@@ -1443,6 +1594,29 @@ ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_opt
return;
}
// Set a Python one-liner as the callback for the watchpoint.
void
ScriptInterpreterPython::SetWatchpointCommandCallback (WatchpointOptions *wp_options,
const char *oneliner)
{
std::auto_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
// It's necessary to set both user_source and script_source to the oneliner.
// The former is used to generate callback description (as in watchpoint command list)
// while the latter is used for Python to interpret during the actual callback.
data_ap->user_source.AppendString (oneliner);
data_ap->script_source.assign (oneliner);
if (GenerateWatchpointCommandCallbackData (data_ap->user_source, data_ap->script_source))
{
BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp);
}
return;
}
bool
ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def)
{
@@ -1661,6 +1835,27 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user
return true;
}
bool
ScriptInterpreterPython::GenerateWatchpointCommandCallbackData (StringList &user_input, std::string& output)
{
static uint32_t num_created_functions = 0;
user_input.RemoveBlankLines ();
StreamString sstr;
if (user_input.GetSize() == 0)
return false;
std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_wp_callback_func_",num_created_functions));
sstr.Printf ("def %s (frame, wp, internal_dict):", auto_generated_function_name.c_str());
if (!GenerateFunction(sstr.GetData(), user_input))
return false;
// Store the name of the auto-generated function to be called.
output.assign(auto_generated_function_name);
return true;
}
bool
ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name,
lldb::ValueObjectSP valobj,
@@ -1764,6 +1959,59 @@ ScriptInterpreterPython::BreakpointCallbackFunction
return true;
}
bool
ScriptInterpreterPython::WatchpointCallbackFunction
(
void *baton,
StoppointCallbackContext *context,
user_id_t watch_id
)
{
WatchpointOptions::CommandData *wp_option_data = (WatchpointOptions::CommandData *) baton;
const char *python_function_name = wp_option_data->script_source.c_str();
if (!context)
return true;
ExecutionContext exe_ctx (context->exe_ctx_ref);
Target *target = exe_ctx.GetTargetPtr();
if (!target)
return true;
Debugger &debugger = target->GetDebugger();
ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter();
ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter;
if (!script_interpreter)
return true;
if (python_function_name != NULL
&& python_function_name[0] != '\0')
{
const StackFrameSP stop_frame_sp (exe_ctx.GetFrameSP());
WatchpointSP wp_sp = target->GetWatchpointList().FindByID (watch_id);
if (wp_sp)
{
if (stop_frame_sp && wp_sp)
{
bool ret_val = true;
{
Locker py_lock(python_interpreter);
ret_val = g_swig_watchpoint_callback (python_function_name,
python_interpreter->m_dictionary_name.c_str(),
stop_frame_sp,
wp_sp);
}
return ret_val;
}
}
}
// We currently always true so we stop in case anything goes wrong when
// trying to call the script function
return true;
}
lldb::thread_result_t
ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton)
{
@@ -2176,6 +2424,7 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_ini
{
g_swig_init_callback = python_swig_init_callback;
g_swig_breakpoint_callback = LLDBSwigPythonBreakpointCallbackFunction;
g_swig_watchpoint_callback = LLDBSwigPythonWatchpointCallbackFunction;
g_swig_typescript_callback = LLDBSwigPythonCallTypeScript;
g_swig_synthetic_script = LLDBSwigPythonCreateSyntheticProvider;
g_swig_calc_children = LLDBSwigPython_CalculateNumChildren;

View File

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

View File

@@ -0,0 +1,93 @@
"""
Test 'watchpoint command'.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
class WatchpointLLDBCommandTestCase(TestBase):
mydir = os.path.join("functionalities", "watchpoint", "watchpoint_commands", "command")
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Our simple source filename.
self.source = 'main.cpp'
# Find the line number to break inside main().
self.line = line_number(self.source, '// Set break point at this line.')
# And the watchpoint variable declaration line number.
self.decl = line_number(self.source, '// Watchpoint variable declaration.')
# Build dictionary to have unique executable names for each test method.
self.exe_name = self.testMethodName
self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name}
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_watchpoint_command_with_dsym(self):
"""Test 'watchpoint command'."""
self.buildDsym(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
self.watchpoint_command()
@dwarf_test
def test_watchpoint_command_with_dwarf(self):
"""Test 'watchpoint command'."""
self.buildDwarf(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
self.watchpoint_command()
def watchpoint_command(self):
"""Do 'watchpoint command add'."""
exe = os.path.join(os.getcwd(), self.exe_name)
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
# Add a breakpoint to set a watchpoint when stopped on the breakpoint.
self.expect("breakpoint set -l %d" % self.line, BREAKPOINT_CREATED,
startstr = "Breakpoint created: 1: file ='%s', line = %d, locations = 1" %
(self.source, self.line))
# Run the program.
self.runCmd("run", RUN_SUCCEEDED)
# We should be stopped again due to the breakpoint.
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# Now let's set a write-type watchpoint for 'global'.
self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED,
substrs = ['Watchpoint created', 'size = 4', 'type = w',
'%s:%d' % (self.source, self.decl)])
self.runCmd('watchpoint command add 1 -o "expr -- global = 777"')
# List the watchpoint command we just added.
self.expect("watchpoint command list 1",
substrs = ['expr -- global = 777'])
# Use the '-v' option to do verbose listing of the watchpoint.
# The hit count should be 0 initially.
self.expect("watchpoint list -v",
substrs = ['hit_count = 0'])
self.runCmd("process continue")
# We should be stopped again due to the watchpoint (write type).
# The stop reason of the thread should be watchpoint.
self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
substrs = ['stop reason = watchpoint'])
# The watchpoint command "forced" our global variable to become 777.
self.expect("frame variable -g global",
substrs = ['(int32_t)', 'global = 777'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@@ -0,0 +1,93 @@
"""
Test 'watchpoint command'.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
class WatchpointPythonCommandTestCase(TestBase):
mydir = os.path.join("functionalities", "watchpoint", "watchpoint_commands", "command")
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Our simple source filename.
self.source = 'main.cpp'
# Find the line number to break inside main().
self.line = line_number(self.source, '// Set break point at this line.')
# And the watchpoint variable declaration line number.
self.decl = line_number(self.source, '// Watchpoint variable declaration.')
# Build dictionary to have unique executable names for each test method.
self.exe_name = self.testMethodName
self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name}
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_watchpoint_command_with_dsym(self):
"""Test 'watchpoint command'."""
self.buildDsym(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
self.watchpoint_command()
@dwarf_test
def test_watchpoint_command_with_dwarf(self):
"""Test 'watchpoint command'."""
self.buildDwarf(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
self.watchpoint_command()
def watchpoint_command(self):
"""Do 'watchpoint command add'."""
exe = os.path.join(os.getcwd(), self.exe_name)
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
# Add a breakpoint to set a watchpoint when stopped on the breakpoint.
self.expect("breakpoint set -l %d" % self.line, BREAKPOINT_CREATED,
startstr = "Breakpoint created: 1: file ='%s', line = %d, locations = 1" %
(self.source, self.line))
# Run the program.
self.runCmd("run", RUN_SUCCEEDED)
# We should be stopped again due to the breakpoint.
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# Now let's set a write-type watchpoint for 'global'.
self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED,
substrs = ['Watchpoint created', 'size = 4', 'type = w',
'%s:%d' % (self.source, self.decl)])
self.runCmd('watchpoint command add -s python 1 -o \'frame.EvaluateExpression("global = 777")\'')
# List the watchpoint command we just added.
self.expect("watchpoint command list 1",
substrs = ['frame.EvaluateExpression', 'global = 777'])
# Use the '-v' option to do verbose listing of the watchpoint.
# The hit count should be 0 initially.
self.expect("watchpoint list -v",
substrs = ['hit_count = 0'])
self.runCmd("process continue")
# We should be stopped again due to the watchpoint (write type).
# The stop reason of the thread should be watchpoint.
self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
substrs = ['stop reason = watchpoint'])
# The watchpoint command "forced" our global variable to become 777.
self.expect("frame variable -g global",
substrs = ['(int32_t)', 'global = 777'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@@ -0,0 +1,28 @@
//===-- 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 <stdio.h>
#include <stdint.h>
int32_t global = 0; // Watchpoint variable declaration.
static void modify(int32_t &var) {
++var;
}
int main(int argc, char** argv) {
int local = 0;
printf("&global=%p\n", &global);
printf("about to write to 'global'...\n"); // Set break point at this line.
// When stopped, watch 'global',
// for the condition "global == 5".
for (int i = 0; i < 10; ++i)
modify(global);
printf("global=%d\n", global);
}