Watchpoint.cpp revision 36da2aa6dc5ad9994b638ed09eb81c44cc05540b
1//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Breakpoint/Watchpoint.h"
11
12// C Includes
13// C++ Includes
14// Other libraries and framework includes
15// Project includes
16#include "lldb/Breakpoint/StoppointCallbackContext.h"
17#include "lldb/Core/Stream.h"
18#include "lldb/Core/Value.h"
19#include "lldb/Core/ValueObject.h"
20#include "lldb/Core/ValueObjectMemory.h"
21#include "lldb/Symbol/ClangASTContext.h"
22#include "lldb/Target/Process.h"
23#include "lldb/Target/Target.h"
24#include "lldb/Target/ThreadSpec.h"
25#include "lldb/Expression/ClangUserExpression.h"
26
27using namespace lldb;
28using namespace lldb_private;
29
30Watchpoint::Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *type, bool hardware) :
31    StoppointLocation (0, addr, size, hardware),
32    m_target(target),
33    m_enabled(false),
34    m_is_hardware(hardware),
35    m_is_watch_variable(false),
36    m_is_ephemeral(false),
37    m_disabled_count(0),
38    m_watch_read(0),
39    m_watch_write(0),
40    m_watch_was_read(0),
41    m_watch_was_written(0),
42    m_ignore_count(0),
43    m_false_alarms(0),
44    m_decl_str(),
45    m_watch_spec_str(),
46    m_type(),
47    m_error(),
48    m_options (),
49    m_being_created(true)
50{
51    if (type && type->IsValid())
52        m_type = *type;
53    else
54    {
55        // If we don't have a known type, then we force it to unsigned int of the right size.
56        ClangASTContext *ast_context = target.GetScratchClangASTContext();
57        clang_type_t clang_type = ast_context->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
58        m_type.SetClangType(ast_context->getASTContext(), clang_type);
59    }
60
61    // Set the initial value of the watched variable:
62    if (m_target.GetProcessSP())
63    {
64        ExecutionContext exe_ctx;
65        m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);
66        CaptureWatchedValue (exe_ctx);
67    }
68    m_being_created = false;
69}
70
71Watchpoint::~Watchpoint()
72{
73}
74
75// This function is used when "baton" doesn't need to be freed
76void
77Watchpoint::SetCallback (WatchpointHitCallback callback, void *baton, bool is_synchronous)
78{
79    // The default "Baton" class will keep a copy of "baton" and won't free
80    // or delete it when it goes goes out of scope.
81    m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous);
82
83    SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged);
84}
85
86// This function is used when a baton needs to be freed and therefore is
87// contained in a "Baton" subclass.
88void
89Watchpoint::SetCallback (WatchpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous)
90{
91    m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
92    SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged);
93}
94
95void
96Watchpoint::ClearCallback ()
97{
98    m_options.ClearCallback ();
99    SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged);
100}
101
102void
103Watchpoint::SetDeclInfo (const std::string &str)
104{
105    m_decl_str = str;
106    return;
107}
108
109std::string
110Watchpoint::GetWatchSpec()
111{
112    return m_watch_spec_str;
113}
114
115void
116Watchpoint::SetWatchSpec (const std::string &str)
117{
118    m_watch_spec_str = str;
119    return;
120}
121
122// Override default impl of StoppointLocation::IsHardware() since m_is_hardware
123// member field is more accurate.
124bool
125Watchpoint::IsHardware () const
126{
127    return m_is_hardware;
128}
129
130bool
131Watchpoint::IsWatchVariable() const
132{
133    return m_is_watch_variable;
134}
135
136void
137Watchpoint::SetWatchVariable(bool val)
138{
139    m_is_watch_variable = val;
140}
141
142bool
143Watchpoint::CaptureWatchedValue (const ExecutionContext &exe_ctx)
144{
145    ConstString watch_name("$__lldb__watch_value");
146    m_old_value_sp = m_new_value_sp;
147    Address watch_address(GetLoadAddress());
148    if (!m_type.IsValid())
149    {
150        // Don't know how to report new & old values, since we couldn't make a scalar type for this watchpoint.
151        // This works around an assert in ValueObjectMemory::Create.
152        // FIXME: This should not happen, but if it does in some case we care about,
153        // we can go grab the value raw and print it as unsigned.
154        return false;
155    }
156    m_new_value_sp = ValueObjectMemory::Create (exe_ctx.GetBestExecutionContextScope(), watch_name.AsCString(), watch_address, m_type);
157    m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name);
158    if (m_new_value_sp && m_new_value_sp->GetError().Success())
159        return true;
160    else
161        return false;
162}
163
164void
165Watchpoint::IncrementFalseAlarmsAndReviseHitCount()
166{
167    ++m_false_alarms;
168    if (m_false_alarms)
169    {
170        if (m_hit_count >= m_false_alarms)
171        {
172            m_hit_count -= m_false_alarms;
173            m_false_alarms = 0;
174        }
175        else
176        {
177            m_false_alarms -= m_hit_count;
178            m_hit_count = 0;
179        }
180    }
181}
182
183// RETURNS - true if we should stop at this breakpoint, false if we
184// should continue.
185
186bool
187Watchpoint::ShouldStop (StoppointCallbackContext *context)
188{
189    IncrementHitCount();
190
191    if (!IsEnabled())
192        return false;
193
194    if (GetHitCount() <= GetIgnoreCount())
195        return false;
196
197    return true;
198}
199
200void
201Watchpoint::GetDescription (Stream *s, lldb::DescriptionLevel level)
202{
203    DumpWithLevel(s, level);
204    return;
205}
206
207void
208Watchpoint::Dump(Stream *s) const
209{
210    DumpWithLevel(s, lldb::eDescriptionLevelBrief);
211}
212
213// If prefix is NULL, we display the watch id and ignore the prefix altogether.
214void
215Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const
216{
217    if (!prefix)
218    {
219        s->Printf("\nWatchpoint %u hit:", GetID());
220        prefix = "";
221    }
222
223    if (m_old_value_sp)
224    {
225        s->Printf("\n%sold value: %s", prefix, m_old_value_sp->GetValueAsCString());
226    }
227    if (m_new_value_sp)
228    {
229        s->Printf("\n%snew value: %s", prefix, m_new_value_sp->GetValueAsCString());
230    }
231}
232
233void
234Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const
235{
236    if (s == NULL)
237        return;
238
239    assert(description_level >= lldb::eDescriptionLevelBrief &&
240           description_level <= lldb::eDescriptionLevelVerbose);
241
242    s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64 " size = %u state = %s type = %s%s",
243              GetID(),
244              GetLoadAddress(),
245              m_byte_size,
246              IsEnabled() ? "enabled" : "disabled",
247              m_watch_read ? "r" : "",
248              m_watch_write ? "w" : "");
249
250    if (description_level >= lldb::eDescriptionLevelFull) {
251        if (!m_decl_str.empty())
252            s->Printf("\n    declare @ '%s'", m_decl_str.c_str());
253        if (!m_watch_spec_str.empty())
254            s->Printf("\n    watchpoint spec = '%s'", m_watch_spec_str.c_str());
255
256        // Dump the snapshots we have taken.
257        DumpSnapshots(s, "    ");
258
259        if (GetConditionText())
260            s->Printf("\n    condition = '%s'", GetConditionText());
261        m_options.GetCallbackDescription(s, description_level);
262    }
263
264    if (description_level >= lldb::eDescriptionLevelVerbose)
265    {
266        s->Printf("\n    hw_index = %i  hit_count = %-4u  ignore_count = %-4u",
267                  GetHardwareIndex(),
268                  GetHitCount(),
269                  GetIgnoreCount());
270    }
271}
272
273bool
274Watchpoint::IsEnabled() const
275{
276    return m_enabled;
277}
278
279// Within StopInfo.cpp, we purposely turn on the ephemeral mode right before temporarily disable the watchpoint
280// in order to perform possible watchpoint actions without triggering further watchpoint events.
281// After the temporary disabled watchpoint is enabled, we then turn off the ephemeral mode.
282
283void
284Watchpoint::TurnOnEphemeralMode()
285{
286    m_is_ephemeral = true;
287}
288
289void
290Watchpoint::TurnOffEphemeralMode()
291{
292    m_is_ephemeral = false;
293    // Leaving ephemeral mode, reset the m_disabled_count!
294    m_disabled_count = 0;
295}
296
297bool
298Watchpoint::IsDisabledDuringEphemeralMode()
299{
300    return m_disabled_count > 1;
301}
302
303void
304Watchpoint::SetEnabled(bool enabled, bool notify)
305{
306    if (!enabled)
307    {
308        if (!m_is_ephemeral)
309            SetHardwareIndex(LLDB_INVALID_INDEX32);
310        else
311            ++m_disabled_count;
312
313        // Don't clear the snapshots for now.
314        // Within StopInfo.cpp, we purposely do disable/enable watchpoint while performing watchpoint actions.
315    }
316    bool changed = enabled != m_enabled;
317    m_enabled = enabled;
318    if (notify && !m_is_ephemeral && changed)
319        SendWatchpointChangedEvent (enabled ? eWatchpointEventTypeEnabled : eWatchpointEventTypeDisabled);
320}
321
322void
323Watchpoint::SetWatchpointType (uint32_t type, bool notify)
324{
325    int old_watch_read = m_watch_read;
326    int old_watch_write = m_watch_write;
327    m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;
328    m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;
329    if (notify && (old_watch_read != m_watch_read || old_watch_write != m_watch_write))
330        SendWatchpointChangedEvent (eWatchpointEventTypeTypeChanged);
331}
332
333bool
334Watchpoint::WatchpointRead () const
335{
336    return m_watch_read != 0;
337}
338bool
339Watchpoint::WatchpointWrite () const
340{
341    return m_watch_write != 0;
342}
343uint32_t
344Watchpoint::GetIgnoreCount () const
345{
346    return m_ignore_count;
347}
348
349void
350Watchpoint::SetIgnoreCount (uint32_t n)
351{
352    bool changed = m_ignore_count != n;
353    m_ignore_count = n;
354    if (changed)
355        SendWatchpointChangedEvent (eWatchpointEventTypeIgnoreChanged);
356}
357
358bool
359Watchpoint::InvokeCallback (StoppointCallbackContext *context)
360{
361    return m_options.InvokeCallback (context, GetID());
362}
363
364void
365Watchpoint::SetCondition (const char *condition)
366{
367    if (condition == NULL || condition[0] == '\0')
368    {
369        if (m_condition_ap.get())
370            m_condition_ap.reset();
371    }
372    else
373    {
374        // Pass NULL for expr_prefix (no translation-unit level definitions).
375        m_condition_ap.reset(new ClangUserExpression (condition, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny));
376    }
377    SendWatchpointChangedEvent (eWatchpointEventTypeConditionChanged);
378}
379
380const char *
381Watchpoint::GetConditionText () const
382{
383    if (m_condition_ap.get())
384        return m_condition_ap->GetUserText();
385    else
386        return NULL;
387}
388
389void
390Watchpoint::SendWatchpointChangedEvent (lldb::WatchpointEventType eventKind)
391{
392    if (!m_being_created
393        && GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged))
394    {
395        WatchpointEventData *data = new Watchpoint::WatchpointEventData (eventKind, shared_from_this());
396        GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, data);
397    }
398}
399
400void
401Watchpoint::SendWatchpointChangedEvent (WatchpointEventData *data)
402{
403
404    if (data == NULL)
405        return;
406
407    if (!m_being_created
408        && GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged))
409        GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, data);
410    else
411        delete data;
412}
413
414Watchpoint::WatchpointEventData::WatchpointEventData (WatchpointEventType sub_type,
415                                                      const WatchpointSP &new_watchpoint_sp) :
416    EventData (),
417    m_watchpoint_event (sub_type),
418    m_new_watchpoint_sp (new_watchpoint_sp)
419{
420}
421
422Watchpoint::WatchpointEventData::~WatchpointEventData ()
423{
424}
425
426const ConstString &
427Watchpoint::WatchpointEventData::GetFlavorString ()
428{
429    static ConstString g_flavor ("Watchpoint::WatchpointEventData");
430    return g_flavor;
431}
432
433const ConstString &
434Watchpoint::WatchpointEventData::GetFlavor () const
435{
436    return WatchpointEventData::GetFlavorString ();
437}
438
439
440WatchpointSP &
441Watchpoint::WatchpointEventData::GetWatchpoint ()
442{
443    return m_new_watchpoint_sp;
444}
445
446WatchpointEventType
447Watchpoint::WatchpointEventData::GetWatchpointEventType () const
448{
449    return m_watchpoint_event;
450}
451
452void
453Watchpoint::WatchpointEventData::Dump (Stream *s) const
454{
455}
456
457const Watchpoint::WatchpointEventData *
458Watchpoint::WatchpointEventData::GetEventDataFromEvent (const Event *event)
459{
460    if (event)
461    {
462        const EventData *event_data = event->GetData();
463        if (event_data && event_data->GetFlavor() == WatchpointEventData::GetFlavorString())
464            return static_cast <const WatchpointEventData *> (event->GetData());
465    }
466    return NULL;
467}
468
469WatchpointEventType
470Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (const EventSP &event_sp)
471{
472    const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get());
473
474    if (data == NULL)
475        return eWatchpointEventTypeInvalidType;
476    else
477        return data->GetWatchpointEventType();
478}
479
480WatchpointSP
481Watchpoint::WatchpointEventData::GetWatchpointFromEvent (const EventSP &event_sp)
482{
483    WatchpointSP wp_sp;
484
485    const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get());
486    if (data)
487        wp_sp = data->m_new_watchpoint_sp;
488
489    return wp_sp;
490}
491