mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 19:08:21 +08:00
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:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
256
lldb/include/lldb/Breakpoint/WatchpointOptions.h
Normal file
256
lldb/include/lldb/Breakpoint/WatchpointOptions.h
Normal 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_
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ class Variable;
|
||||
class VariableList;
|
||||
class Watchpoint;
|
||||
class WatchpointList;
|
||||
class WatchpointOptions;
|
||||
struct LineEntry;
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
(
|
||||
|
||||
@@ -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
|
||||
|
||||
244
lldb/source/Breakpoint/WatchpointOptions.cpp
Normal file
244
lldb/source/Breakpoint/WatchpointOptions.cpp
Normal 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 ();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
849
lldb/source/Commands/CommandObjectWatchpointCommand.cpp
Normal file
849
lldb/source/Commands/CommandObjectWatchpointCommand.cpp
Normal 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 ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
46
lldb/source/Commands/CommandObjectWatchpointCommand.h
Normal file
46
lldb/source/Commands/CommandObjectWatchpointCommand.h
Normal 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_
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
LEVEL = ../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user