1 //===-- CommandObjectWatchpoint.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/lldb-python.h"
11 
12 #include "CommandObjectWatchpoint.h"
13 #include "CommandObjectWatchpointCommand.h"
14 
15 // C Includes
16 // C++ Includes
17 // Other libraries and framework includes
18 // Project includes
19 #include "lldb/Breakpoint/Watchpoint.h"
20 #include "lldb/Breakpoint/WatchpointList.h"
21 #include "lldb/Core/StreamString.h"
22 #include "lldb/Core/ValueObject.h"
23 #include "lldb/Core/ValueObjectVariable.h"
24 #include "lldb/Interpreter/CommandInterpreter.h"
25 #include "lldb/Interpreter/CommandReturnObject.h"
26 #include "lldb/Interpreter/CommandCompletions.h"
27 #include "lldb/Symbol/Variable.h"
28 #include "lldb/Symbol/VariableList.h"
29 #include "lldb/Target/Target.h"
30 
31 #include <vector>
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 
36 static void
37 AddWatchpointDescription(Stream *s, Watchpoint *wp, lldb::DescriptionLevel level)
38 {
39     s->IndentMore();
40     wp->GetDescription(s, level);
41     s->IndentLess();
42     s->EOL();
43 }
44 
45 static bool
46 CheckTargetForWatchpointOperations(Target *target, CommandReturnObject &result)
47 {
48     if (target == NULL)
49     {
50         result.AppendError ("Invalid target.  No existing target or watchpoints.");
51         result.SetStatus (eReturnStatusFailed);
52         return false;
53     }
54     bool process_is_valid = target->GetProcessSP() && target->GetProcessSP()->IsAlive();
55     if (!process_is_valid)
56     {
57         result.AppendError ("Thre's no process or it is not alive.");
58         result.SetStatus (eReturnStatusFailed);
59         return false;
60     }
61     // Target passes our checks, return true.
62     return true;
63 }
64 
65 // FIXME: This doesn't seem to be the right place for this functionality.
66 #include "llvm/ADT/StringRef.h"
67 static inline void StripLeadingSpaces(llvm::StringRef &Str)
68 {
69     while (!Str.empty() && isspace(Str[0]))
70         Str = Str.substr(1);
71 }
72 static inline llvm::StringRef StripOptionTerminator(llvm::StringRef &Str, bool with_dash_w, bool with_dash_x)
73 {
74     llvm::StringRef ExprStr = Str;
75 
76     // Get rid of the leading spaces first.
77     StripLeadingSpaces(ExprStr);
78 
79     // If there's no '-w' and no '-x', we can just return.
80     if (!with_dash_w && !with_dash_x)
81         return ExprStr;
82 
83     // Otherwise, split on the "--" option terminator string, and return the rest of the string.
84     ExprStr = ExprStr.split("--").second;
85     StripLeadingSpaces(ExprStr);
86     return ExprStr;
87 }
88 
89 
90 // Equivalent class: {"-", "to", "To", "TO"} of range specifier array.
91 static const char* RSA[4] = { "-", "to", "To", "TO" };
92 
93 // Return the index to RSA if found; otherwise -1 is returned.
94 static int32_t
95 WithRSAIndex(llvm::StringRef &Arg)
96 {
97 
98     uint32_t i;
99     for (i = 0; i < 4; ++i)
100         if (Arg.find(RSA[i]) != llvm::StringRef::npos)
101             return i;
102     return -1;
103 }
104 
105 // Return true if wp_ids is successfully populated with the watch ids.
106 // False otherwise.
107 bool
108 CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(Args &args, std::vector<uint32_t> &wp_ids)
109 {
110     // Pre-condition: args.GetArgumentCount() > 0.
111     assert(args.GetArgumentCount() > 0);
112 
113     llvm::StringRef Minus("-");
114     std::vector<llvm::StringRef> StrRefArgs;
115     std::pair<llvm::StringRef, llvm::StringRef> Pair;
116     size_t i;
117     int32_t idx;
118     // Go through the argments and make a canonical form of arg list containing
119     // only numbers with possible "-" in between.
120     for (i = 0; i < args.GetArgumentCount(); ++i) {
121         llvm::StringRef Arg(args.GetArgumentAtIndex(i));
122         if ((idx = WithRSAIndex(Arg)) == -1) {
123             StrRefArgs.push_back(Arg);
124             continue;
125         }
126         // The Arg contains the range specifier, split it, then.
127         Pair = Arg.split(RSA[idx]);
128         if (!Pair.first.empty())
129             StrRefArgs.push_back(Pair.first);
130         StrRefArgs.push_back(Minus);
131         if (!Pair.second.empty())
132             StrRefArgs.push_back(Pair.second);
133     }
134     // Now process the canonical list and fill in the vector of uint32_t's.
135     // If there is any error, return false and the client should ignore wp_ids.
136     uint32_t beg, end, id;
137     size_t size = StrRefArgs.size();
138     bool in_range = false;
139     for (i = 0; i < size; ++i) {
140         llvm::StringRef Arg = StrRefArgs[i];
141         if (in_range) {
142             // Look for the 'end' of the range.  Note StringRef::getAsInteger()
143             // returns true to signify error while parsing.
144             if (Arg.getAsInteger(0, end))
145                 return false;
146             // Found a range!  Now append the elements.
147             for (id = beg; id <= end; ++id)
148                 wp_ids.push_back(id);
149             in_range = false;
150             continue;
151         }
152         if (i < (size - 1) && StrRefArgs[i+1] == Minus) {
153             if (Arg.getAsInteger(0, beg))
154                 return false;
155             // Turn on the in_range flag, we are looking for end of range next.
156             ++i; in_range = true;
157             continue;
158         }
159         // Otherwise, we have a simple ID.  Just append it.
160         if (Arg.getAsInteger(0, beg))
161             return false;
162         wp_ids.push_back(beg);
163     }
164     // It is an error if after the loop, we're still in_range.
165     if (in_range)
166         return false;
167 
168     return true; // Success!
169 }
170 
171 //-------------------------------------------------------------------------
172 // CommandObjectWatchpointList
173 //-------------------------------------------------------------------------
174 #pragma mark List
175 
176 class CommandObjectWatchpointList : public CommandObjectParsed
177 {
178 public:
179     CommandObjectWatchpointList (CommandInterpreter &interpreter) :
180         CommandObjectParsed (interpreter,
181                              "watchpoint list",
182                              "List all watchpoints at configurable levels of detail.",
183                              NULL),
184         m_options(interpreter)
185     {
186         CommandArgumentEntry arg;
187         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
188         // Add the entry for the first argument for this command to the object's arguments vector.
189         m_arguments.push_back(arg);
190     }
191 
192     virtual
193     ~CommandObjectWatchpointList () {}
194 
195     virtual Options *
196     GetOptions ()
197     {
198         return &m_options;
199     }
200 
201     class CommandOptions : public Options
202     {
203     public:
204 
205         CommandOptions (CommandInterpreter &interpreter) :
206             Options(interpreter),
207             m_level(lldb::eDescriptionLevelBrief) // Watchpoint List defaults to brief descriptions
208         {
209         }
210 
211         virtual
212         ~CommandOptions () {}
213 
214         virtual Error
215         SetOptionValue (uint32_t option_idx, const char *option_arg)
216         {
217             Error error;
218             const int short_option = m_getopt_table[option_idx].val;
219 
220             switch (short_option)
221             {
222                 case 'b':
223                     m_level = lldb::eDescriptionLevelBrief;
224                     break;
225                 case 'f':
226                     m_level = lldb::eDescriptionLevelFull;
227                     break;
228                 case 'v':
229                     m_level = lldb::eDescriptionLevelVerbose;
230                     break;
231                 default:
232                     error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
233                     break;
234             }
235 
236             return error;
237         }
238 
239         void
240         OptionParsingStarting ()
241         {
242             m_level = lldb::eDescriptionLevelFull;
243         }
244 
245         const OptionDefinition *
246         GetDefinitions ()
247         {
248             return g_option_table;
249         }
250 
251 
252         // Options table: Required for subclasses of Options.
253 
254         static OptionDefinition g_option_table[];
255 
256         // Instance variables to hold the values for command options.
257 
258         lldb::DescriptionLevel m_level;
259     };
260 
261 protected:
262     virtual bool
263     DoExecute (Args& command, CommandReturnObject &result)
264     {
265         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
266         if (target == NULL)
267         {
268             result.AppendError ("Invalid target. No current target or watchpoints.");
269             result.SetStatus (eReturnStatusSuccessFinishNoResult);
270             return true;
271         }
272 
273         if (target->GetProcessSP() && target->GetProcessSP()->IsAlive())
274         {
275             uint32_t num_supported_hardware_watchpoints;
276             Error error = target->GetProcessSP()->GetWatchpointSupportInfo(num_supported_hardware_watchpoints);
277             if (error.Success())
278                 result.AppendMessageWithFormat("Number of supported hardware watchpoints: %u\n",
279                                                num_supported_hardware_watchpoints);
280         }
281 
282         const WatchpointList &watchpoints = target->GetWatchpointList();
283         Mutex::Locker locker;
284         target->GetWatchpointList().GetListMutex(locker);
285 
286         size_t num_watchpoints = watchpoints.GetSize();
287 
288         if (num_watchpoints == 0)
289         {
290             result.AppendMessage("No watchpoints currently set.");
291             result.SetStatus(eReturnStatusSuccessFinishNoResult);
292             return true;
293         }
294 
295         Stream &output_stream = result.GetOutputStream();
296 
297         if (command.GetArgumentCount() == 0)
298         {
299             // No watchpoint selected; show info about all currently set watchpoints.
300             result.AppendMessage ("Current watchpoints:");
301             for (size_t i = 0; i < num_watchpoints; ++i)
302             {
303                 Watchpoint *wp = watchpoints.GetByIndex(i).get();
304                 AddWatchpointDescription(&output_stream, wp, m_options.m_level);
305             }
306             result.SetStatus(eReturnStatusSuccessFinishNoResult);
307         }
308         else
309         {
310             // Particular watchpoints selected; enable them.
311             std::vector<uint32_t> wp_ids;
312             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
313             {
314                 result.AppendError("Invalid watchpoints specification.");
315                 result.SetStatus(eReturnStatusFailed);
316                 return false;
317             }
318 
319             const size_t size = wp_ids.size();
320             for (size_t i = 0; i < size; ++i)
321             {
322                 Watchpoint *wp = watchpoints.FindByID(wp_ids[i]).get();
323                 if (wp)
324                     AddWatchpointDescription(&output_stream, wp, m_options.m_level);
325                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
326             }
327         }
328 
329         return result.Succeeded();
330     }
331 
332 private:
333     CommandOptions m_options;
334 };
335 
336 //-------------------------------------------------------------------------
337 // CommandObjectWatchpointList::Options
338 //-------------------------------------------------------------------------
339 #pragma mark List::CommandOptions
340 OptionDefinition
341 CommandObjectWatchpointList::CommandOptions::g_option_table[] =
342 {
343     { LLDB_OPT_SET_1, false, "brief",    'b', no_argument, NULL, 0, eArgTypeNone,
344         "Give a brief description of the watchpoint (no location info)."},
345 
346     { LLDB_OPT_SET_2, false, "full",    'f', no_argument, NULL, 0, eArgTypeNone,
347         "Give a full description of the watchpoint and its locations."},
348 
349     { LLDB_OPT_SET_3, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone,
350         "Explain everything we know about the watchpoint (for debugging debugger bugs)." },
351 
352     { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
353 };
354 
355 //-------------------------------------------------------------------------
356 // CommandObjectWatchpointEnable
357 //-------------------------------------------------------------------------
358 #pragma mark Enable
359 
360 class CommandObjectWatchpointEnable : public CommandObjectParsed
361 {
362 public:
363     CommandObjectWatchpointEnable (CommandInterpreter &interpreter) :
364         CommandObjectParsed (interpreter,
365                              "enable",
366                              "Enable the specified disabled watchpoint(s). If no watchpoints are specified, enable all of them.",
367                              NULL)
368     {
369         CommandArgumentEntry arg;
370         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
371         // Add the entry for the first argument for this command to the object's arguments vector.
372         m_arguments.push_back(arg);
373     }
374 
375     virtual
376     ~CommandObjectWatchpointEnable () {}
377 
378 protected:
379     virtual bool
380     DoExecute (Args& command,
381              CommandReturnObject &result)
382     {
383         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
384         if (!CheckTargetForWatchpointOperations(target, result))
385             return false;
386 
387         Mutex::Locker locker;
388         target->GetWatchpointList().GetListMutex(locker);
389 
390         const WatchpointList &watchpoints = target->GetWatchpointList();
391 
392         size_t num_watchpoints = watchpoints.GetSize();
393 
394         if (num_watchpoints == 0)
395         {
396             result.AppendError("No watchpoints exist to be enabled.");
397             result.SetStatus(eReturnStatusFailed);
398             return false;
399         }
400 
401         if (command.GetArgumentCount() == 0)
402         {
403             // No watchpoint selected; enable all currently set watchpoints.
404             target->EnableAllWatchpoints();
405             result.AppendMessageWithFormat("All watchpoints enabled. (%lu watchpoints)\n", num_watchpoints);
406             result.SetStatus(eReturnStatusSuccessFinishNoResult);
407         }
408         else
409         {
410             // Particular watchpoints selected; enable them.
411             std::vector<uint32_t> wp_ids;
412             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
413             {
414                 result.AppendError("Invalid watchpoints specification.");
415                 result.SetStatus(eReturnStatusFailed);
416                 return false;
417             }
418 
419             int count = 0;
420             const size_t size = wp_ids.size();
421             for (size_t i = 0; i < size; ++i)
422                 if (target->EnableWatchpointByID(wp_ids[i]))
423                     ++count;
424             result.AppendMessageWithFormat("%d watchpoints enabled.\n", count);
425             result.SetStatus(eReturnStatusSuccessFinishNoResult);
426         }
427 
428         return result.Succeeded();
429     }
430 
431 private:
432 };
433 
434 //-------------------------------------------------------------------------
435 // CommandObjectWatchpointDisable
436 //-------------------------------------------------------------------------
437 #pragma mark Disable
438 
439 class CommandObjectWatchpointDisable : public CommandObjectParsed
440 {
441 public:
442     CommandObjectWatchpointDisable (CommandInterpreter &interpreter) :
443         CommandObjectParsed (interpreter,
444                              "watchpoint disable",
445                              "Disable the specified watchpoint(s) without removing it/them.  If no watchpoints are specified, disable them all.",
446                              NULL)
447     {
448         CommandArgumentEntry arg;
449         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
450         // Add the entry for the first argument for this command to the object's arguments vector.
451         m_arguments.push_back(arg);
452     }
453 
454 
455     virtual
456     ~CommandObjectWatchpointDisable () {}
457 
458 protected:
459     virtual bool
460     DoExecute (Args& command, CommandReturnObject &result)
461     {
462         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
463         if (!CheckTargetForWatchpointOperations(target, result))
464             return false;
465 
466         Mutex::Locker locker;
467         target->GetWatchpointList().GetListMutex(locker);
468 
469         const WatchpointList &watchpoints = target->GetWatchpointList();
470         size_t num_watchpoints = watchpoints.GetSize();
471 
472         if (num_watchpoints == 0)
473         {
474             result.AppendError("No watchpoints exist to be disabled.");
475             result.SetStatus(eReturnStatusFailed);
476             return false;
477         }
478 
479         if (command.GetArgumentCount() == 0)
480         {
481             // No watchpoint selected; disable all currently set watchpoints.
482             if (target->DisableAllWatchpoints())
483             {
484                 result.AppendMessageWithFormat("All watchpoints disabled. (%lu watchpoints)\n", num_watchpoints);
485                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
486             }
487             else
488             {
489                 result.AppendError("Disable all watchpoints failed\n");
490                 result.SetStatus(eReturnStatusFailed);
491             }
492         }
493         else
494         {
495             // Particular watchpoints selected; disable them.
496             std::vector<uint32_t> wp_ids;
497             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
498             {
499                 result.AppendError("Invalid watchpoints specification.");
500                 result.SetStatus(eReturnStatusFailed);
501                 return false;
502             }
503 
504             int count = 0;
505             const size_t size = wp_ids.size();
506             for (size_t i = 0; i < size; ++i)
507                 if (target->DisableWatchpointByID(wp_ids[i]))
508                     ++count;
509             result.AppendMessageWithFormat("%d watchpoints disabled.\n", count);
510             result.SetStatus(eReturnStatusSuccessFinishNoResult);
511         }
512 
513         return result.Succeeded();
514     }
515 
516 };
517 
518 //-------------------------------------------------------------------------
519 // CommandObjectWatchpointDelete
520 //-------------------------------------------------------------------------
521 #pragma mark Delete
522 
523 class CommandObjectWatchpointDelete : public CommandObjectParsed
524 {
525 public:
526     CommandObjectWatchpointDelete (CommandInterpreter &interpreter) :
527         CommandObjectParsed(interpreter,
528                             "watchpoint delete",
529                             "Delete the specified watchpoint(s).  If no watchpoints are specified, delete them all.",
530                             NULL)
531     {
532         CommandArgumentEntry arg;
533         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
534         // Add the entry for the first argument for this command to the object's arguments vector.
535         m_arguments.push_back(arg);
536     }
537 
538     virtual
539     ~CommandObjectWatchpointDelete () {}
540 
541 protected:
542     virtual bool
543     DoExecute (Args& command, CommandReturnObject &result)
544     {
545         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
546         if (!CheckTargetForWatchpointOperations(target, result))
547             return false;
548 
549         Mutex::Locker locker;
550         target->GetWatchpointList().GetListMutex(locker);
551 
552         const WatchpointList &watchpoints = target->GetWatchpointList();
553 
554         size_t num_watchpoints = watchpoints.GetSize();
555 
556         if (num_watchpoints == 0)
557         {
558             result.AppendError("No watchpoints exist to be deleted.");
559             result.SetStatus(eReturnStatusFailed);
560             return false;
561         }
562 
563         if (command.GetArgumentCount() == 0)
564         {
565             if (!m_interpreter.Confirm("About to delete all watchpoints, do you want to do that?", true))
566             {
567                 result.AppendMessage("Operation cancelled...");
568             }
569             else
570             {
571                 target->RemoveAllWatchpoints();
572                 result.AppendMessageWithFormat("All watchpoints removed. (%lu watchpoints)\n", num_watchpoints);
573             }
574             result.SetStatus (eReturnStatusSuccessFinishNoResult);
575         }
576         else
577         {
578             // Particular watchpoints selected; delete them.
579             std::vector<uint32_t> wp_ids;
580             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
581             {
582                 result.AppendError("Invalid watchpoints specification.");
583                 result.SetStatus(eReturnStatusFailed);
584                 return false;
585             }
586 
587             int count = 0;
588             const size_t size = wp_ids.size();
589             for (size_t i = 0; i < size; ++i)
590                 if (target->RemoveWatchpointByID(wp_ids[i]))
591                     ++count;
592             result.AppendMessageWithFormat("%d watchpoints deleted.\n",count);
593             result.SetStatus (eReturnStatusSuccessFinishNoResult);
594         }
595 
596         return result.Succeeded();
597     }
598 
599 };
600 
601 //-------------------------------------------------------------------------
602 // CommandObjectWatchpointIgnore
603 //-------------------------------------------------------------------------
604 
605 class CommandObjectWatchpointIgnore : public CommandObjectParsed
606 {
607 public:
608     CommandObjectWatchpointIgnore (CommandInterpreter &interpreter) :
609         CommandObjectParsed (interpreter,
610                              "watchpoint ignore",
611                              "Set ignore count on the specified watchpoint(s).  If no watchpoints are specified, set them all.",
612                              NULL),
613         m_options (interpreter)
614     {
615         CommandArgumentEntry arg;
616         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
617         // Add the entry for the first argument for this command to the object's arguments vector.
618         m_arguments.push_back(arg);
619     }
620 
621     virtual
622     ~CommandObjectWatchpointIgnore () {}
623 
624     virtual Options *
625     GetOptions ()
626     {
627         return &m_options;
628     }
629 
630     class CommandOptions : public Options
631     {
632     public:
633 
634         CommandOptions (CommandInterpreter &interpreter) :
635             Options (interpreter),
636             m_ignore_count (0)
637         {
638         }
639 
640         virtual
641         ~CommandOptions () {}
642 
643         virtual Error
644         SetOptionValue (uint32_t option_idx, const char *option_arg)
645         {
646             Error error;
647             const int short_option = m_getopt_table[option_idx].val;
648 
649             switch (short_option)
650             {
651                 case 'i':
652                 {
653                     m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0);
654                     if (m_ignore_count == UINT32_MAX)
655                        error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg);
656                 }
657                 break;
658                 default:
659                     error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
660                     break;
661             }
662 
663             return error;
664         }
665 
666         void
667         OptionParsingStarting ()
668         {
669             m_ignore_count = 0;
670         }
671 
672         const OptionDefinition *
673         GetDefinitions ()
674         {
675             return g_option_table;
676         }
677 
678 
679         // Options table: Required for subclasses of Options.
680 
681         static OptionDefinition g_option_table[];
682 
683         // Instance variables to hold the values for command options.
684 
685         uint32_t m_ignore_count;
686     };
687 
688 protected:
689     virtual bool
690     DoExecute (Args& command,
691              CommandReturnObject &result)
692     {
693         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
694         if (!CheckTargetForWatchpointOperations(target, result))
695             return false;
696 
697         Mutex::Locker locker;
698         target->GetWatchpointList().GetListMutex(locker);
699 
700         const WatchpointList &watchpoints = target->GetWatchpointList();
701 
702         size_t num_watchpoints = watchpoints.GetSize();
703 
704         if (num_watchpoints == 0)
705         {
706             result.AppendError("No watchpoints exist to be ignored.");
707             result.SetStatus(eReturnStatusFailed);
708             return false;
709         }
710 
711         if (command.GetArgumentCount() == 0)
712         {
713             target->IgnoreAllWatchpoints(m_options.m_ignore_count);
714             result.AppendMessageWithFormat("All watchpoints ignored. (%lu watchpoints)\n", num_watchpoints);
715             result.SetStatus (eReturnStatusSuccessFinishNoResult);
716         }
717         else
718         {
719             // Particular watchpoints selected; ignore them.
720             std::vector<uint32_t> wp_ids;
721             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
722             {
723                 result.AppendError("Invalid watchpoints specification.");
724                 result.SetStatus(eReturnStatusFailed);
725                 return false;
726             }
727 
728             int count = 0;
729             const size_t size = wp_ids.size();
730             for (size_t i = 0; i < size; ++i)
731                 if (target->IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count))
732                     ++count;
733             result.AppendMessageWithFormat("%d watchpoints ignored.\n",count);
734             result.SetStatus (eReturnStatusSuccessFinishNoResult);
735         }
736 
737         return result.Succeeded();
738     }
739 
740 private:
741     CommandOptions m_options;
742 };
743 
744 #pragma mark Ignore::CommandOptions
745 OptionDefinition
746 CommandObjectWatchpointIgnore::CommandOptions::g_option_table[] =
747 {
748     { LLDB_OPT_SET_ALL, true, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, "Set the number of times this watchpoint is skipped before stopping." },
749     { 0,                false, NULL,            0 , 0,                 NULL, 0,    eArgTypeNone, NULL }
750 };
751 
752 
753 //-------------------------------------------------------------------------
754 // CommandObjectWatchpointModify
755 //-------------------------------------------------------------------------
756 #pragma mark Modify
757 
758 class CommandObjectWatchpointModify : public CommandObjectParsed
759 {
760 public:
761 
762     CommandObjectWatchpointModify (CommandInterpreter &interpreter) :
763         CommandObjectParsed (interpreter,
764                              "watchpoint modify",
765                              "Modify the options on a watchpoint or set of watchpoints in the executable.  "
766                              "If no watchpoint is specified, act on the last created watchpoint.  "
767                              "Passing an empty argument clears the modification.",
768                              NULL),
769         m_options (interpreter)
770     {
771         CommandArgumentEntry arg;
772         CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange);
773         // Add the entry for the first argument for this command to the object's arguments vector.
774         m_arguments.push_back (arg);
775     }
776 
777     virtual
778     ~CommandObjectWatchpointModify () {}
779 
780     virtual Options *
781     GetOptions ()
782     {
783         return &m_options;
784     }
785 
786     class CommandOptions : public Options
787     {
788     public:
789 
790         CommandOptions (CommandInterpreter &interpreter) :
791             Options (interpreter),
792             m_condition (),
793             m_condition_passed (false)
794         {
795         }
796 
797         virtual
798         ~CommandOptions () {}
799 
800         virtual Error
801         SetOptionValue (uint32_t option_idx, const char *option_arg)
802         {
803             Error error;
804             const int short_option = m_getopt_table[option_idx].val;
805 
806             switch (short_option)
807             {
808                 case 'c':
809                     if (option_arg != NULL)
810                         m_condition.assign (option_arg);
811                     else
812                         m_condition.clear();
813                     m_condition_passed = true;
814                     break;
815                 default:
816                     error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
817                     break;
818             }
819 
820             return error;
821         }
822 
823         void
824         OptionParsingStarting ()
825         {
826             m_condition.clear();
827             m_condition_passed = false;
828         }
829 
830         const OptionDefinition*
831         GetDefinitions ()
832         {
833             return g_option_table;
834         }
835 
836         // Options table: Required for subclasses of Options.
837 
838         static OptionDefinition g_option_table[];
839 
840         // Instance variables to hold the values for command options.
841 
842         std::string m_condition;
843         bool m_condition_passed;
844     };
845 
846 protected:
847     virtual bool
848     DoExecute (Args& command, CommandReturnObject &result)
849     {
850         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
851         if (!CheckTargetForWatchpointOperations(target, result))
852             return false;
853 
854         Mutex::Locker locker;
855         target->GetWatchpointList().GetListMutex(locker);
856 
857         const WatchpointList &watchpoints = target->GetWatchpointList();
858 
859         size_t num_watchpoints = watchpoints.GetSize();
860 
861         if (num_watchpoints == 0)
862         {
863             result.AppendError("No watchpoints exist to be modified.");
864             result.SetStatus(eReturnStatusFailed);
865             return false;
866         }
867 
868         if (command.GetArgumentCount() == 0)
869         {
870             WatchpointSP wp_sp = target->GetLastCreatedWatchpoint();
871             wp_sp->SetCondition(m_options.m_condition.c_str());
872             result.SetStatus (eReturnStatusSuccessFinishNoResult);
873         }
874         else
875         {
876             // Particular watchpoints selected; set condition on them.
877             std::vector<uint32_t> wp_ids;
878             if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(command, wp_ids))
879             {
880                 result.AppendError("Invalid watchpoints specification.");
881                 result.SetStatus(eReturnStatusFailed);
882                 return false;
883             }
884 
885             int count = 0;
886             const size_t size = wp_ids.size();
887             for (size_t i = 0; i < size; ++i)
888             {
889                 WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]);
890                 if (wp_sp)
891                 {
892                     wp_sp->SetCondition(m_options.m_condition.c_str());
893                     ++count;
894                 }
895             }
896             result.AppendMessageWithFormat("%d watchpoints modified.\n",count);
897             result.SetStatus (eReturnStatusSuccessFinishNoResult);
898         }
899 
900         return result.Succeeded();
901     }
902 
903 private:
904     CommandOptions m_options;
905 };
906 
907 #pragma mark Modify::CommandOptions
908 OptionDefinition
909 CommandObjectWatchpointModify::CommandOptions::g_option_table[] =
910 {
911 { LLDB_OPT_SET_ALL, false, "condition",    'c', required_argument, NULL, 0, eArgTypeExpression, "The watchpoint stops only if this condition expression evaluates to true."},
912 { 0,                false, NULL,            0 , 0,                 NULL, 0,    eArgTypeNone, NULL }
913 };
914 
915 //-------------------------------------------------------------------------
916 // CommandObjectWatchpointSetVariable
917 //-------------------------------------------------------------------------
918 #pragma mark SetVariable
919 
920 class CommandObjectWatchpointSetVariable : public CommandObjectParsed
921 {
922 public:
923 
924     CommandObjectWatchpointSetVariable (CommandInterpreter &interpreter) :
925         CommandObjectParsed (interpreter,
926                              "watchpoint set variable",
927                              "Set a watchpoint on a variable. "
928                              "Use the '-w' option to specify the type of watchpoint and "
929                              "the '-x' option to specify the byte size to watch for. "
930                              "If no '-w' option is specified, it defaults to write. "
931                              "If no '-x' option is specified, it defaults to the variable's "
932                              "byte size. "
933                              "Note that there are limited hardware resources for watchpoints. "
934                              "If watchpoint setting fails, consider disable/delete existing ones "
935                              "to free up resources.",
936                              NULL,
937                              eFlagRequiresFrame         |
938                              eFlagTryTargetAPILock      |
939                              eFlagProcessMustBeLaunched |
940                              eFlagProcessMustBePaused   ),
941         m_option_group (interpreter),
942         m_option_watchpoint ()
943     {
944         SetHelpLong(
945     "Examples: \n\
946     \n\
947         watchpoint set variable -w read_wriate my_global_var \n\
948         # Watch my_global_var for read/write access, with the region to watch corresponding to the byte size of the data type.\n");
949 
950         CommandArgumentEntry arg;
951         CommandArgumentData var_name_arg;
952 
953         // Define the only variant of this arg.
954         var_name_arg.arg_type = eArgTypeVarName;
955         var_name_arg.arg_repetition = eArgRepeatPlain;
956 
957         // Push the variant into the argument entry.
958         arg.push_back (var_name_arg);
959 
960         // Push the data for the only argument into the m_arguments vector.
961         m_arguments.push_back (arg);
962 
963         // Absorb the '-w' and '-x' options into our option group.
964         m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
965         m_option_group.Finalize();
966     }
967 
968     virtual
969     ~CommandObjectWatchpointSetVariable () {}
970 
971     virtual Options *
972     GetOptions ()
973     {
974         return &m_option_group;
975     }
976 
977 protected:
978     static size_t GetVariableCallback (void *baton,
979                                        const char *name,
980                                        VariableList &variable_list)
981     {
982         Target *target = static_cast<Target *>(baton);
983         if (target)
984         {
985             return target->GetImages().FindGlobalVariables (ConstString(name),
986                                                             true,
987                                                             UINT32_MAX,
988                                                             variable_list);
989         }
990         return 0;
991     }
992 
993     virtual bool
994     DoExecute (Args& command, CommandReturnObject &result)
995     {
996         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
997         StackFrame *frame = m_exe_ctx.GetFramePtr();
998 
999         // If no argument is present, issue an error message.  There's no way to set a watchpoint.
1000         if (command.GetArgumentCount() <= 0)
1001         {
1002             result.GetErrorStream().Printf("error: required argument missing; specify your program variable to watch for\n");
1003             result.SetStatus(eReturnStatusFailed);
1004             return false;
1005         }
1006 
1007         // If no '-w' is specified, default to '-w write'.
1008         if (!m_option_watchpoint.watch_type_specified)
1009         {
1010             m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
1011         }
1012 
1013         // We passed the sanity check for the command.
1014         // Proceed to set the watchpoint now.
1015         lldb::addr_t addr = 0;
1016         size_t size = 0;
1017 
1018         VariableSP var_sp;
1019         ValueObjectSP valobj_sp;
1020         Stream &output_stream = result.GetOutputStream();
1021 
1022         // A simple watch variable gesture allows only one argument.
1023         if (command.GetArgumentCount() != 1)
1024         {
1025             result.GetErrorStream().Printf("error: specify exactly one variable to watch for\n");
1026             result.SetStatus(eReturnStatusFailed);
1027             return false;
1028         }
1029 
1030         // Things have checked out ok...
1031         Error error;
1032         uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember |
1033                                      StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
1034         valobj_sp = frame->GetValueForVariableExpressionPath (command.GetArgumentAtIndex(0),
1035                                                               eNoDynamicValues,
1036                                                               expr_path_options,
1037                                                               var_sp,
1038                                                               error);
1039 
1040         if (!valobj_sp)
1041         {
1042             // Not in the frame; let's check the globals.
1043 
1044             VariableList variable_list;
1045             ValueObjectList valobj_list;
1046 
1047             Error error (Variable::GetValuesForVariableExpressionPath (command.GetArgumentAtIndex(0),
1048                                                                        m_exe_ctx.GetBestExecutionContextScope(),
1049                                                                        GetVariableCallback,
1050                                                                        target,
1051                                                                        variable_list,
1052                                                                        valobj_list));
1053 
1054             if (valobj_list.GetSize())
1055                 valobj_sp = valobj_list.GetValueObjectAtIndex(0);
1056         }
1057 
1058         ClangASTType type;
1059 
1060         if (valobj_sp)
1061         {
1062             AddressType addr_type;
1063             addr = valobj_sp->GetAddressOf(false, &addr_type);
1064             if (addr_type == eAddressTypeLoad)
1065             {
1066                 // We're in business.
1067                 // Find out the size of this variable.
1068                 size = m_option_watchpoint.watch_size == 0 ? valobj_sp->GetByteSize()
1069                                                            : m_option_watchpoint.watch_size;
1070             }
1071             type.SetClangType(valobj_sp->GetClangAST(), valobj_sp->GetClangType());
1072         }
1073         else
1074         {
1075             const char *error_cstr = error.AsCString(NULL);
1076             if (error_cstr)
1077                 result.GetErrorStream().Printf("error: %s\n", error_cstr);
1078             else
1079                 result.GetErrorStream().Printf ("error: unable to find any variable expression path that matches '%s'\n",
1080                                                 command.GetArgumentAtIndex(0));
1081             return false;
1082         }
1083 
1084         // Now it's time to create the watchpoint.
1085         uint32_t watch_type = m_option_watchpoint.watch_type;
1086         error.Clear();
1087         Watchpoint *wp = target->CreateWatchpoint(addr, size, &type, watch_type, error).get();
1088         if (wp)
1089         {
1090             wp->SetWatchSpec(command.GetArgumentAtIndex(0));
1091             wp->SetWatchVariable(true);
1092             if (var_sp && var_sp->GetDeclaration().GetFile())
1093             {
1094                 StreamString ss;
1095                 // True to show fullpath for declaration file.
1096                 var_sp->GetDeclaration().DumpStopContext(&ss, true);
1097                 wp->SetDeclInfo(ss.GetString());
1098             }
1099             output_stream.Printf("Watchpoint created: ");
1100             wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
1101             output_stream.EOL();
1102             result.SetStatus(eReturnStatusSuccessFinishResult);
1103         }
1104         else
1105         {
1106             result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 ", size=%lu, variable expression='%s').\n",
1107                                          addr, size, command.GetArgumentAtIndex(0));
1108             if (error.AsCString(NULL))
1109                 result.AppendError(error.AsCString());
1110             result.SetStatus(eReturnStatusFailed);
1111         }
1112 
1113         return result.Succeeded();
1114     }
1115 
1116 private:
1117     OptionGroupOptions m_option_group;
1118     OptionGroupWatchpoint m_option_watchpoint;
1119 };
1120 
1121 //-------------------------------------------------------------------------
1122 // CommandObjectWatchpointSetExpression
1123 //-------------------------------------------------------------------------
1124 #pragma mark Set
1125 
1126 class CommandObjectWatchpointSetExpression : public CommandObjectRaw
1127 {
1128 public:
1129 
1130     CommandObjectWatchpointSetExpression (CommandInterpreter &interpreter) :
1131         CommandObjectRaw (interpreter,
1132                           "watchpoint set expression",
1133                           "Set a watchpoint on an address by supplying an expression. "
1134                           "Use the '-w' option to specify the type of watchpoint and "
1135                           "the '-x' option to specify the byte size to watch for. "
1136                           "If no '-w' option is specified, it defaults to write. "
1137                           "If no '-x' option is specified, it defaults to the target's "
1138                           "pointer byte size. "
1139                           "Note that there are limited hardware resources for watchpoints. "
1140                           "If watchpoint setting fails, consider disable/delete existing ones "
1141                           "to free up resources.",
1142                           NULL,
1143                           eFlagRequiresFrame         |
1144                           eFlagTryTargetAPILock      |
1145                           eFlagProcessMustBeLaunched |
1146                           eFlagProcessMustBePaused   ),
1147         m_option_group (interpreter),
1148         m_option_watchpoint ()
1149     {
1150         SetHelpLong(
1151     "Examples: \n\
1152     \n\
1153         watchpoint set expression -w write -x 1 -- foo + 32\n\
1154         # Watch write access for the 1-byte region pointed to by the address 'foo + 32'.\n");
1155 
1156         CommandArgumentEntry arg;
1157         CommandArgumentData expression_arg;
1158 
1159         // Define the only variant of this arg.
1160         expression_arg.arg_type = eArgTypeExpression;
1161         expression_arg.arg_repetition = eArgRepeatPlain;
1162 
1163         // Push the only variant into the argument entry.
1164         arg.push_back (expression_arg);
1165 
1166         // Push the data for the only argument into the m_arguments vector.
1167         m_arguments.push_back (arg);
1168 
1169         // Absorb the '-w' and '-x' options into our option group.
1170         m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
1171         m_option_group.Finalize();
1172     }
1173 
1174 
1175     virtual
1176     ~CommandObjectWatchpointSetExpression () {}
1177 
1178     // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
1179     virtual bool
1180     WantsCompletion() { return true; }
1181 
1182     virtual Options *
1183     GetOptions ()
1184     {
1185         return &m_option_group;
1186     }
1187 
1188 protected:
1189     virtual bool
1190     DoExecute (const char *raw_command, CommandReturnObject &result)
1191     {
1192         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
1193         StackFrame *frame = m_exe_ctx.GetFramePtr();
1194 
1195         Args command(raw_command);
1196 
1197         // Process possible options.
1198         if (!ParseOptions (command, result))
1199             return false;
1200 
1201         // If no argument is present, issue an error message.  There's no way to set a watchpoint.
1202         if (command.GetArgumentCount() <= 0)
1203         {
1204             result.GetErrorStream().Printf("error: required argument missing; specify an expression to evaulate into the addres to watch for\n");
1205             result.SetStatus(eReturnStatusFailed);
1206             return false;
1207         }
1208 
1209         bool with_dash_w = m_option_watchpoint.watch_type_specified;
1210         bool with_dash_x = (m_option_watchpoint.watch_size != 0);
1211 
1212         // If no '-w' is specified, default to '-w write'.
1213         if (!with_dash_w)
1214         {
1215             m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
1216         }
1217 
1218         // We passed the sanity check for the command.
1219         // Proceed to set the watchpoint now.
1220         lldb::addr_t addr = 0;
1221         size_t size = 0;
1222 
1223         VariableSP var_sp;
1224         ValueObjectSP valobj_sp;
1225         Stream &output_stream = result.GetOutputStream();
1226 
1227         // We will process the raw command string to rid of the '-w', '-x', or '--'
1228         llvm::StringRef raw_expr_str(raw_command);
1229         std::string expr_str = StripOptionTerminator(raw_expr_str, with_dash_w, with_dash_x).str();
1230 
1231         // Sanity check for when the user forgets to terminate the option strings with a '--'.
1232         if ((with_dash_w || with_dash_w) && expr_str.empty())
1233         {
1234             result.GetErrorStream().Printf("error: did you forget to enter the option terminator string \"--\"?\n");
1235             result.SetStatus(eReturnStatusFailed);
1236             return false;
1237         }
1238 
1239         // Use expression evaluation to arrive at the address to watch.
1240         EvaluateExpressionOptions options;
1241         options.SetCoerceToId(false)
1242         .SetUnwindOnError(true)
1243         .SetKeepInMemory(false)
1244         .SetRunOthers(true)
1245         .SetTimeoutUsec(0);
1246 
1247         ExecutionResults expr_result = target->EvaluateExpression (expr_str.c_str(),
1248                                                                    frame,
1249                                                                    valobj_sp,
1250                                                                    options);
1251         if (expr_result != eExecutionCompleted)
1252         {
1253             result.GetErrorStream().Printf("error: expression evaluation of address to watch failed\n");
1254             result.GetErrorStream().Printf("expression evaluated: %s\n", expr_str.c_str());
1255             result.SetStatus(eReturnStatusFailed);
1256             return false;
1257         }
1258 
1259         // Get the address to watch.
1260         bool success = false;
1261         addr = valobj_sp->GetValueAsUnsigned(0, &success);
1262         if (!success)
1263         {
1264             result.GetErrorStream().Printf("error: expression did not evaluate to an address\n");
1265             result.SetStatus(eReturnStatusFailed);
1266             return false;
1267         }
1268         size = with_dash_x ? m_option_watchpoint.watch_size
1269                            : target->GetArchitecture().GetAddressByteSize();
1270 
1271         // Now it's time to create the watchpoint.
1272         uint32_t watch_type = m_option_watchpoint.watch_type;
1273 
1274         // Fetch the type from the value object, the type of the watched object is the pointee type
1275         /// of the expression, so convert to that if we  found a valid type.
1276         ClangASTType type(valobj_sp->GetClangAST(), valobj_sp->GetClangType());
1277         if (type.IsValid())
1278             type.SetClangType(type.GetASTContext(), type.GetPointeeType());
1279 
1280         Error error;
1281         Watchpoint *wp = target->CreateWatchpoint(addr, size, &type, watch_type, error).get();
1282         if (wp)
1283         {
1284             if (var_sp && var_sp->GetDeclaration().GetFile())
1285             {
1286                 StreamString ss;
1287                 // True to show fullpath for declaration file.
1288                 var_sp->GetDeclaration().DumpStopContext(&ss, true);
1289                 wp->SetDeclInfo(ss.GetString());
1290             }
1291             output_stream.Printf("Watchpoint created: ");
1292             wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
1293             output_stream.EOL();
1294             result.SetStatus(eReturnStatusSuccessFinishResult);
1295         }
1296         else
1297         {
1298             result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 ", size=%lu).\n",
1299                                          addr, size);
1300             if (error.AsCString(NULL))
1301                 result.AppendError(error.AsCString());
1302             result.SetStatus(eReturnStatusFailed);
1303         }
1304 
1305         return result.Succeeded();
1306     }
1307 
1308 private:
1309     OptionGroupOptions m_option_group;
1310     OptionGroupWatchpoint m_option_watchpoint;
1311 };
1312 
1313 //-------------------------------------------------------------------------
1314 // CommandObjectWatchpointSet
1315 //-------------------------------------------------------------------------
1316 #pragma mark Set
1317 
1318 class CommandObjectWatchpointSet : public CommandObjectMultiword
1319 {
1320 public:
1321 
1322     CommandObjectWatchpointSet (CommandInterpreter &interpreter) :
1323         CommandObjectMultiword (interpreter,
1324                                 "watchpoint set",
1325                                 "A set of commands for setting a watchpoint.",
1326                                 "watchpoint set <subcommand> [<subcommand-options>]")
1327     {
1328 
1329         LoadSubCommand ("variable",   CommandObjectSP (new CommandObjectWatchpointSetVariable (interpreter)));
1330         LoadSubCommand ("expression", CommandObjectSP (new CommandObjectWatchpointSetExpression (interpreter)));
1331     }
1332 
1333 
1334     virtual
1335     ~CommandObjectWatchpointSet () {}
1336 
1337 };
1338 
1339 //-------------------------------------------------------------------------
1340 // CommandObjectMultiwordWatchpoint
1341 //-------------------------------------------------------------------------
1342 #pragma mark MultiwordWatchpoint
1343 
1344 CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterpreter &interpreter) :
1345     CommandObjectMultiword (interpreter,
1346                             "watchpoint",
1347                             "A set of commands for operating on watchpoints.",
1348                             "watchpoint <command> [<command-options>]")
1349 {
1350     CommandObjectSP list_command_object (new CommandObjectWatchpointList (interpreter));
1351     CommandObjectSP enable_command_object (new CommandObjectWatchpointEnable (interpreter));
1352     CommandObjectSP disable_command_object (new CommandObjectWatchpointDisable (interpreter));
1353     CommandObjectSP delete_command_object (new CommandObjectWatchpointDelete (interpreter));
1354     CommandObjectSP ignore_command_object (new CommandObjectWatchpointIgnore (interpreter));
1355     CommandObjectSP command_command_object (new CommandObjectWatchpointCommand (interpreter));
1356     CommandObjectSP modify_command_object (new CommandObjectWatchpointModify (interpreter));
1357     CommandObjectSP set_command_object (new CommandObjectWatchpointSet (interpreter));
1358 
1359     list_command_object->SetCommandName ("watchpoint list");
1360     enable_command_object->SetCommandName("watchpoint enable");
1361     disable_command_object->SetCommandName("watchpoint disable");
1362     delete_command_object->SetCommandName("watchpoint delete");
1363     ignore_command_object->SetCommandName("watchpoint ignore");
1364     command_command_object->SetCommandName ("watchpoint command");
1365     modify_command_object->SetCommandName("watchpoint modify");
1366     set_command_object->SetCommandName("watchpoint set");
1367 
1368     LoadSubCommand ("list",       list_command_object);
1369     LoadSubCommand ("enable",     enable_command_object);
1370     LoadSubCommand ("disable",    disable_command_object);
1371     LoadSubCommand ("delete",     delete_command_object);
1372     LoadSubCommand ("ignore",     ignore_command_object);
1373     LoadSubCommand ("command",    command_command_object);
1374     LoadSubCommand ("modify",     modify_command_object);
1375     LoadSubCommand ("set",        set_command_object);
1376 }
1377 
1378 CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint()
1379 {
1380 }
1381 
1382