OLD | NEW |
| (Empty) |
1 // Copyright 2012 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef V8_DEBUG_H_ | |
6 #define V8_DEBUG_H_ | |
7 | |
8 #include "src/allocation.h" | |
9 #include "src/arguments.h" | |
10 #include "src/assembler.h" | |
11 #include "src/base/atomicops.h" | |
12 #include "src/base/platform/platform.h" | |
13 #include "src/execution.h" | |
14 #include "src/factory.h" | |
15 #include "src/flags.h" | |
16 #include "src/frames-inl.h" | |
17 #include "src/hashmap.h" | |
18 #include "src/liveedit.h" | |
19 #include "src/runtime/runtime.h" | |
20 #include "src/string-stream.h" | |
21 #include "src/v8threads.h" | |
22 | |
23 #include "include/v8-debug.h" | |
24 | |
25 namespace v8 { | |
26 namespace internal { | |
27 | |
28 | |
29 // Forward declarations. | |
30 class DebugScope; | |
31 | |
32 | |
33 // Step actions. NOTE: These values are in macros.py as well. | |
34 enum StepAction { | |
35 StepNone = -1, // Stepping not prepared. | |
36 StepOut = 0, // Step out of the current function. | |
37 StepNext = 1, // Step to the next statement in the current function. | |
38 StepIn = 2, // Step into new functions invoked or the next statement | |
39 // in the current function. | |
40 StepMin = 3, // Perform a minimum step in the current function. | |
41 StepInMin = 4, // Step into new functions invoked or perform a minimum step | |
42 // in the current function. | |
43 StepFrame = 5 // Step into a new frame or return to previous frame. | |
44 }; | |
45 | |
46 | |
47 // Type of exception break. NOTE: These values are in macros.py as well. | |
48 enum ExceptionBreakType { | |
49 BreakException = 0, | |
50 BreakUncaughtException = 1 | |
51 }; | |
52 | |
53 | |
54 // Type of exception break. | |
55 enum BreakLocatorType { ALL_BREAK_LOCATIONS, CALLS_AND_RETURNS }; | |
56 | |
57 | |
58 // The different types of breakpoint position alignments. | |
59 // Must match Debug.BreakPositionAlignment in debug-debugger.js | |
60 enum BreakPositionAlignment { | |
61 STATEMENT_ALIGNED = 0, | |
62 BREAK_POSITION_ALIGNED = 1 | |
63 }; | |
64 | |
65 | |
66 class BreakLocation { | |
67 public: | |
68 // Find the break point at the supplied address, or the closest one before | |
69 // the address. | |
70 static BreakLocation FromAddress(Handle<DebugInfo> debug_info, | |
71 BreakLocatorType type, Address pc); | |
72 | |
73 static void FromAddressSameStatement(Handle<DebugInfo> debug_info, | |
74 BreakLocatorType type, Address pc, | |
75 List<BreakLocation>* result_out); | |
76 | |
77 static BreakLocation FromPosition(Handle<DebugInfo> debug_info, | |
78 BreakLocatorType type, int position, | |
79 BreakPositionAlignment alignment); | |
80 | |
81 bool IsDebugBreak() const; | |
82 | |
83 inline bool IsReturn() const { | |
84 return RelocInfo::IsDebugBreakSlotAtReturn(rmode_); | |
85 } | |
86 inline bool IsCall() const { | |
87 return RelocInfo::IsDebugBreakSlotAtCall(rmode_); | |
88 } | |
89 inline bool IsConstructCall() const { | |
90 return RelocInfo::IsDebugBreakSlotAtConstructCall(rmode_); | |
91 } | |
92 inline int CallArgumentsCount() const { | |
93 DCHECK(IsCall()); | |
94 return RelocInfo::DebugBreakCallArgumentsCount(data_); | |
95 } | |
96 | |
97 bool IsStepInLocation() const; | |
98 inline bool HasBreakPoint() const { | |
99 return debug_info_->HasBreakPoint(pc_offset_); | |
100 } | |
101 | |
102 Handle<Object> BreakPointObjects() const; | |
103 | |
104 void SetBreakPoint(Handle<Object> break_point_object); | |
105 void ClearBreakPoint(Handle<Object> break_point_object); | |
106 | |
107 void SetOneShot(); | |
108 void ClearOneShot(); | |
109 | |
110 | |
111 inline RelocInfo rinfo() const { | |
112 return RelocInfo(pc(), rmode(), data_, code()); | |
113 } | |
114 | |
115 inline int position() const { return position_; } | |
116 inline int statement_position() const { return statement_position_; } | |
117 | |
118 inline Address pc() const { return code()->entry() + pc_offset_; } | |
119 | |
120 inline RelocInfo::Mode rmode() const { return rmode_; } | |
121 | |
122 inline Code* code() const { return debug_info_->code(); } | |
123 | |
124 private: | |
125 BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, int position, | |
126 int statement_position); | |
127 | |
128 class Iterator { | |
129 public: | |
130 Iterator(Handle<DebugInfo> debug_info, BreakLocatorType type); | |
131 | |
132 BreakLocation GetBreakLocation() { | |
133 return BreakLocation(debug_info_, rinfo(), position(), | |
134 statement_position()); | |
135 } | |
136 | |
137 inline bool Done() const { return reloc_iterator_.done(); } | |
138 void Next(); | |
139 | |
140 void SkipTo(int count) { | |
141 while (count-- > 0) Next(); | |
142 } | |
143 | |
144 inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); } | |
145 inline RelocInfo* rinfo() { return reloc_iterator_.rinfo(); } | |
146 inline Address pc() { return rinfo()->pc(); } | |
147 int break_index() const { return break_index_; } | |
148 inline int position() const { return position_; } | |
149 inline int statement_position() const { return statement_position_; } | |
150 | |
151 private: | |
152 static int GetModeMask(BreakLocatorType type); | |
153 | |
154 Handle<DebugInfo> debug_info_; | |
155 RelocIterator reloc_iterator_; | |
156 int break_index_; | |
157 int position_; | |
158 int statement_position_; | |
159 | |
160 DisallowHeapAllocation no_gc_; | |
161 | |
162 DISALLOW_COPY_AND_ASSIGN(Iterator); | |
163 }; | |
164 | |
165 friend class Debug; | |
166 | |
167 static int BreakIndexFromAddress(Handle<DebugInfo> debug_info, | |
168 BreakLocatorType type, Address pc); | |
169 | |
170 void SetDebugBreak(); | |
171 void ClearDebugBreak(); | |
172 | |
173 inline bool IsDebuggerStatement() const { | |
174 return RelocInfo::IsDebuggerStatement(rmode_); | |
175 } | |
176 inline bool IsDebugBreakSlot() const { | |
177 return RelocInfo::IsDebugBreakSlot(rmode_); | |
178 } | |
179 | |
180 Handle<DebugInfo> debug_info_; | |
181 int pc_offset_; | |
182 RelocInfo::Mode rmode_; | |
183 intptr_t data_; | |
184 int position_; | |
185 int statement_position_; | |
186 }; | |
187 | |
188 | |
189 // Cache of all script objects in the heap. When a script is added a weak handle | |
190 // to it is created and that weak handle is stored in the cache. The weak handle | |
191 // callback takes care of removing the script from the cache. The key used in | |
192 // the cache is the script id. | |
193 class ScriptCache { | |
194 public: | |
195 explicit ScriptCache(Isolate* isolate); | |
196 ~ScriptCache(); | |
197 | |
198 // Add script to the cache. | |
199 void Add(Handle<Script> script); | |
200 | |
201 // Return the scripts in the cache. | |
202 Handle<FixedArray> GetScripts() { | |
203 return WeakValueHashTable::GetWeakValues(table_); | |
204 } | |
205 | |
206 private: | |
207 Isolate* isolate_; | |
208 Handle<WeakValueHashTable> table_; | |
209 }; | |
210 | |
211 | |
212 // Linked list holding debug info objects. The debug info objects are kept as | |
213 // weak handles to avoid a debug info object to keep a function alive. | |
214 class DebugInfoListNode { | |
215 public: | |
216 explicit DebugInfoListNode(DebugInfo* debug_info); | |
217 ~DebugInfoListNode(); | |
218 | |
219 DebugInfoListNode* next() { return next_; } | |
220 void set_next(DebugInfoListNode* next) { next_ = next; } | |
221 Handle<DebugInfo> debug_info() { return Handle<DebugInfo>(debug_info_); } | |
222 | |
223 private: | |
224 // Global (weak) handle to the debug info object. | |
225 DebugInfo** debug_info_; | |
226 | |
227 // Next pointer for linked list. | |
228 DebugInfoListNode* next_; | |
229 }; | |
230 | |
231 | |
232 | |
233 // Message delivered to the message handler callback. This is either a debugger | |
234 // event or the response to a command. | |
235 class MessageImpl: public v8::Debug::Message { | |
236 public: | |
237 // Create a message object for a debug event. | |
238 static MessageImpl NewEvent(DebugEvent event, | |
239 bool running, | |
240 Handle<JSObject> exec_state, | |
241 Handle<JSObject> event_data); | |
242 | |
243 // Create a message object for the response to a debug command. | |
244 static MessageImpl NewResponse(DebugEvent event, | |
245 bool running, | |
246 Handle<JSObject> exec_state, | |
247 Handle<JSObject> event_data, | |
248 Handle<String> response_json, | |
249 v8::Debug::ClientData* client_data); | |
250 | |
251 // Implementation of interface v8::Debug::Message. | |
252 virtual bool IsEvent() const; | |
253 virtual bool IsResponse() const; | |
254 virtual DebugEvent GetEvent() const; | |
255 virtual bool WillStartRunning() const; | |
256 virtual v8::Local<v8::Object> GetExecutionState() const; | |
257 virtual v8::Local<v8::Object> GetEventData() const; | |
258 virtual v8::Local<v8::String> GetJSON() const; | |
259 virtual v8::Local<v8::Context> GetEventContext() const; | |
260 virtual v8::Debug::ClientData* GetClientData() const; | |
261 virtual v8::Isolate* GetIsolate() const; | |
262 | |
263 private: | |
264 MessageImpl(bool is_event, | |
265 DebugEvent event, | |
266 bool running, | |
267 Handle<JSObject> exec_state, | |
268 Handle<JSObject> event_data, | |
269 Handle<String> response_json, | |
270 v8::Debug::ClientData* client_data); | |
271 | |
272 bool is_event_; // Does this message represent a debug event? | |
273 DebugEvent event_; // Debug event causing the break. | |
274 bool running_; // Will the VM start running after this event? | |
275 Handle<JSObject> exec_state_; // Current execution state. | |
276 Handle<JSObject> event_data_; // Data associated with the event. | |
277 Handle<String> response_json_; // Response JSON if message holds a response. | |
278 v8::Debug::ClientData* client_data_; // Client data passed with the request. | |
279 }; | |
280 | |
281 | |
282 // Details of the debug event delivered to the debug event listener. | |
283 class EventDetailsImpl : public v8::Debug::EventDetails { | |
284 public: | |
285 EventDetailsImpl(DebugEvent event, | |
286 Handle<JSObject> exec_state, | |
287 Handle<JSObject> event_data, | |
288 Handle<Object> callback_data, | |
289 v8::Debug::ClientData* client_data); | |
290 virtual DebugEvent GetEvent() const; | |
291 virtual v8::Local<v8::Object> GetExecutionState() const; | |
292 virtual v8::Local<v8::Object> GetEventData() const; | |
293 virtual v8::Local<v8::Context> GetEventContext() const; | |
294 virtual v8::Local<v8::Value> GetCallbackData() const; | |
295 virtual v8::Debug::ClientData* GetClientData() const; | |
296 private: | |
297 DebugEvent event_; // Debug event causing the break. | |
298 Handle<JSObject> exec_state_; // Current execution state. | |
299 Handle<JSObject> event_data_; // Data associated with the event. | |
300 Handle<Object> callback_data_; // User data passed with the callback | |
301 // when it was registered. | |
302 v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand. | |
303 }; | |
304 | |
305 | |
306 // Message send by user to v8 debugger or debugger output message. | |
307 // In addition to command text it may contain a pointer to some user data | |
308 // which are expected to be passed along with the command reponse to message | |
309 // handler. | |
310 class CommandMessage { | |
311 public: | |
312 static CommandMessage New(const Vector<uint16_t>& command, | |
313 v8::Debug::ClientData* data); | |
314 CommandMessage(); | |
315 | |
316 // Deletes user data and disposes of the text. | |
317 void Dispose(); | |
318 Vector<uint16_t> text() const { return text_; } | |
319 v8::Debug::ClientData* client_data() const { return client_data_; } | |
320 private: | |
321 CommandMessage(const Vector<uint16_t>& text, | |
322 v8::Debug::ClientData* data); | |
323 | |
324 Vector<uint16_t> text_; | |
325 v8::Debug::ClientData* client_data_; | |
326 }; | |
327 | |
328 | |
329 // A Queue of CommandMessage objects. A thread-safe version is | |
330 // LockingCommandMessageQueue, based on this class. | |
331 class CommandMessageQueue BASE_EMBEDDED { | |
332 public: | |
333 explicit CommandMessageQueue(int size); | |
334 ~CommandMessageQueue(); | |
335 bool IsEmpty() const { return start_ == end_; } | |
336 CommandMessage Get(); | |
337 void Put(const CommandMessage& message); | |
338 void Clear() { start_ = end_ = 0; } // Queue is empty after Clear(). | |
339 private: | |
340 // Doubles the size of the message queue, and copies the messages. | |
341 void Expand(); | |
342 | |
343 CommandMessage* messages_; | |
344 int start_; | |
345 int end_; | |
346 int size_; // The size of the queue buffer. Queue can hold size-1 messages. | |
347 }; | |
348 | |
349 | |
350 // LockingCommandMessageQueue is a thread-safe circular buffer of CommandMessage | |
351 // messages. The message data is not managed by LockingCommandMessageQueue. | |
352 // Pointers to the data are passed in and out. Implemented by adding a | |
353 // Mutex to CommandMessageQueue. Includes logging of all puts and gets. | |
354 class LockingCommandMessageQueue BASE_EMBEDDED { | |
355 public: | |
356 LockingCommandMessageQueue(Logger* logger, int size); | |
357 bool IsEmpty() const; | |
358 CommandMessage Get(); | |
359 void Put(const CommandMessage& message); | |
360 void Clear(); | |
361 private: | |
362 Logger* logger_; | |
363 CommandMessageQueue queue_; | |
364 mutable base::Mutex mutex_; | |
365 DISALLOW_COPY_AND_ASSIGN(LockingCommandMessageQueue); | |
366 }; | |
367 | |
368 | |
369 // This class contains the debugger support. The main purpose is to handle | |
370 // setting break points in the code. | |
371 // | |
372 // This class controls the debug info for all functions which currently have | |
373 // active breakpoints in them. This debug info is held in the heap root object | |
374 // debug_info which is a FixedArray. Each entry in this list is of class | |
375 // DebugInfo. | |
376 class Debug { | |
377 public: | |
378 // Debug event triggers. | |
379 void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue); | |
380 | |
381 void OnThrow(Handle<Object> exception); | |
382 void OnPromiseReject(Handle<JSObject> promise, Handle<Object> value); | |
383 void OnCompileError(Handle<Script> script); | |
384 void OnBeforeCompile(Handle<Script> script); | |
385 void OnAfterCompile(Handle<Script> script); | |
386 void OnPromiseEvent(Handle<JSObject> data); | |
387 void OnAsyncTaskEvent(Handle<JSObject> data); | |
388 | |
389 // API facing. | |
390 void SetEventListener(Handle<Object> callback, Handle<Object> data); | |
391 void SetMessageHandler(v8::Debug::MessageHandler handler); | |
392 void EnqueueCommandMessage(Vector<const uint16_t> command, | |
393 v8::Debug::ClientData* client_data = NULL); | |
394 MUST_USE_RESULT MaybeHandle<Object> Call(Handle<JSFunction> fun, | |
395 Handle<Object> data); | |
396 Handle<Context> GetDebugContext(); | |
397 void HandleDebugBreak(); | |
398 void ProcessDebugMessages(bool debug_command_only); | |
399 | |
400 // Internal logic | |
401 bool Load(); | |
402 void Break(Arguments args, JavaScriptFrame*); | |
403 void SetAfterBreakTarget(JavaScriptFrame* frame); | |
404 | |
405 // Scripts handling. | |
406 Handle<FixedArray> GetLoadedScripts(); | |
407 | |
408 // Break point handling. | |
409 bool SetBreakPoint(Handle<JSFunction> function, | |
410 Handle<Object> break_point_object, | |
411 int* source_position); | |
412 bool SetBreakPointForScript(Handle<Script> script, | |
413 Handle<Object> break_point_object, | |
414 int* source_position, | |
415 BreakPositionAlignment alignment); | |
416 void ClearBreakPoint(Handle<Object> break_point_object); | |
417 void ClearAllBreakPoints(); | |
418 void FloodWithOneShot(Handle<JSFunction> function, | |
419 BreakLocatorType type = ALL_BREAK_LOCATIONS); | |
420 void FloodBoundFunctionWithOneShot(Handle<JSFunction> function); | |
421 void FloodDefaultConstructorWithOneShot(Handle<JSFunction> function); | |
422 void FloodWithOneShotGeneric(Handle<JSFunction> function, | |
423 Handle<Object> holder = Handle<Object>()); | |
424 void FloodHandlerWithOneShot(); | |
425 void ChangeBreakOnException(ExceptionBreakType type, bool enable); | |
426 bool IsBreakOnException(ExceptionBreakType type); | |
427 | |
428 // Stepping handling. | |
429 void PrepareStep(StepAction step_action, | |
430 int step_count, | |
431 StackFrame::Id frame_id); | |
432 void ClearStepping(); | |
433 void ClearStepOut(); | |
434 bool IsStepping() { return thread_local_.step_count_ > 0; } | |
435 bool StepNextContinue(BreakLocation* location, JavaScriptFrame* frame); | |
436 bool StepInActive() { return thread_local_.step_into_fp_ != 0; } | |
437 void HandleStepIn(Handle<Object> function_obj, bool is_constructor); | |
438 bool StepOutActive() { return thread_local_.step_out_fp_ != 0; } | |
439 | |
440 void GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id, | |
441 List<int>* results_out); | |
442 | |
443 bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared); | |
444 | |
445 // Returns whether the operation succeeded. Compilation can only be triggered | |
446 // if a valid closure is passed as the second argument, otherwise the shared | |
447 // function needs to be compiled already. | |
448 bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared, | |
449 Handle<JSFunction> function); | |
450 static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared); | |
451 | |
452 template <typename C> | |
453 bool CompileToRevealInnerFunctions(C* compilable); | |
454 | |
455 // This function is used in FunctionNameUsing* tests. | |
456 Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script, | |
457 int position); | |
458 | |
459 // Returns true if the current stub call is patched to call the debugger. | |
460 static bool IsDebugBreak(Address addr); | |
461 | |
462 static Handle<Object> GetSourceBreakLocations( | |
463 Handle<SharedFunctionInfo> shared, | |
464 BreakPositionAlignment position_aligment); | |
465 | |
466 // Check whether a global object is the debug global object. | |
467 bool IsDebugGlobal(GlobalObject* global); | |
468 | |
469 // Check whether this frame is just about to return. | |
470 bool IsBreakAtReturn(JavaScriptFrame* frame); | |
471 | |
472 // Support for LiveEdit | |
473 void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, | |
474 LiveEdit::FrameDropMode mode, | |
475 Object** restarter_frame_function_pointer); | |
476 | |
477 // Threading support. | |
478 char* ArchiveDebug(char* to); | |
479 char* RestoreDebug(char* from); | |
480 static int ArchiveSpacePerThread(); | |
481 void FreeThreadResources() { } | |
482 | |
483 // Record function from which eval was called. | |
484 static void RecordEvalCaller(Handle<Script> script); | |
485 | |
486 bool CheckExecutionState(int id) { | |
487 return is_active() && !debug_context().is_null() && break_id() != 0 && | |
488 break_id() == id; | |
489 } | |
490 | |
491 // Flags and states. | |
492 DebugScope* debugger_entry() { | |
493 return reinterpret_cast<DebugScope*>( | |
494 base::NoBarrier_Load(&thread_local_.current_debug_scope_)); | |
495 } | |
496 inline Handle<Context> debug_context() { return debug_context_; } | |
497 void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; } | |
498 bool live_edit_enabled() const { | |
499 return FLAG_enable_liveedit && live_edit_enabled_ ; | |
500 } | |
501 | |
502 inline bool is_active() const { return is_active_; } | |
503 inline bool is_loaded() const { return !debug_context_.is_null(); } | |
504 inline bool in_debug_scope() const { | |
505 return !!base::NoBarrier_Load(&thread_local_.current_debug_scope_); | |
506 } | |
507 void set_disable_break(bool v) { break_disabled_ = v; } | |
508 | |
509 StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; } | |
510 int break_id() { return thread_local_.break_id_; } | |
511 | |
512 // Support for embedding into generated code. | |
513 Address is_active_address() { | |
514 return reinterpret_cast<Address>(&is_active_); | |
515 } | |
516 | |
517 Address after_break_target_address() { | |
518 return reinterpret_cast<Address>(&after_break_target_); | |
519 } | |
520 | |
521 Address restarter_frame_function_pointer_address() { | |
522 Object*** address = &thread_local_.restarter_frame_function_pointer_; | |
523 return reinterpret_cast<Address>(address); | |
524 } | |
525 | |
526 Address step_in_fp_addr() { | |
527 return reinterpret_cast<Address>(&thread_local_.step_into_fp_); | |
528 } | |
529 | |
530 StepAction last_step_action() { return thread_local_.last_step_action_; } | |
531 | |
532 private: | |
533 explicit Debug(Isolate* isolate); | |
534 | |
535 void UpdateState(); | |
536 void Unload(); | |
537 void SetNextBreakId() { | |
538 thread_local_.break_id_ = ++thread_local_.break_count_; | |
539 } | |
540 | |
541 // Check whether there are commands in the command queue. | |
542 inline bool has_commands() const { return !command_queue_.IsEmpty(); } | |
543 inline bool ignore_events() const { return is_suppressed_ || !is_active_; } | |
544 inline bool break_disabled() const { | |
545 return break_disabled_ || in_debug_event_listener_; | |
546 } | |
547 | |
548 void OnException(Handle<Object> exception, Handle<Object> promise); | |
549 | |
550 // Constructors for debug event objects. | |
551 MUST_USE_RESULT MaybeHandle<Object> MakeJSObject( | |
552 const char* constructor_name, | |
553 int argc, | |
554 Handle<Object> argv[]); | |
555 MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState(); | |
556 MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent( | |
557 Handle<Object> break_points_hit); | |
558 MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent( | |
559 Handle<Object> exception, | |
560 bool uncaught, | |
561 Handle<Object> promise); | |
562 MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent( | |
563 Handle<Script> script, v8::DebugEvent type); | |
564 MUST_USE_RESULT MaybeHandle<Object> MakePromiseEvent( | |
565 Handle<JSObject> promise_event); | |
566 MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent( | |
567 Handle<JSObject> task_event); | |
568 | |
569 // Mirror cache handling. | |
570 void ClearMirrorCache(); | |
571 | |
572 MaybeHandle<Object> PromiseHasUserDefinedRejectHandler( | |
573 Handle<JSObject> promise); | |
574 | |
575 void CallEventCallback(v8::DebugEvent event, | |
576 Handle<Object> exec_state, | |
577 Handle<Object> event_data, | |
578 v8::Debug::ClientData* client_data); | |
579 void ProcessCompileEventInDebugScope(v8::DebugEvent event, | |
580 Handle<Script> script); | |
581 void ProcessDebugEvent(v8::DebugEvent event, | |
582 Handle<JSObject> event_data, | |
583 bool auto_continue); | |
584 void NotifyMessageHandler(v8::DebugEvent event, | |
585 Handle<JSObject> exec_state, | |
586 Handle<JSObject> event_data, | |
587 bool auto_continue); | |
588 void InvokeMessageHandler(MessageImpl message); | |
589 | |
590 static bool CompileDebuggerScript(Isolate* isolate, int index); | |
591 void ClearOneShot(); | |
592 void ActivateStepIn(StackFrame* frame); | |
593 void ClearStepIn(); | |
594 void ActivateStepOut(StackFrame* frame); | |
595 void ClearStepNext(); | |
596 void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info); | |
597 Handle<Object> CheckBreakPoints(Handle<Object> break_point); | |
598 bool CheckBreakPoint(Handle<Object> break_point_object); | |
599 | |
600 inline void AssertDebugContext() { | |
601 DCHECK(isolate_->context() == *debug_context()); | |
602 DCHECK(in_debug_scope()); | |
603 } | |
604 | |
605 void ThreadInit(); | |
606 | |
607 // Global handles. | |
608 Handle<Context> debug_context_; | |
609 Handle<Object> event_listener_; | |
610 Handle<Object> event_listener_data_; | |
611 | |
612 v8::Debug::MessageHandler message_handler_; | |
613 | |
614 static const int kQueueInitialSize = 4; | |
615 base::Semaphore command_received_; // Signaled for each command received. | |
616 LockingCommandMessageQueue command_queue_; | |
617 | |
618 bool is_active_; | |
619 bool is_suppressed_; | |
620 bool live_edit_enabled_; | |
621 bool has_break_points_; | |
622 bool break_disabled_; | |
623 bool in_debug_event_listener_; | |
624 bool break_on_exception_; | |
625 bool break_on_uncaught_exception_; | |
626 | |
627 ScriptCache* script_cache_; // Cache of all scripts in the heap. | |
628 DebugInfoListNode* debug_info_list_; // List of active debug info objects. | |
629 | |
630 // Storage location for jump when exiting debug break calls. | |
631 // Note that this address is not GC safe. It should be computed immediately | |
632 // before returning to the DebugBreakCallHelper. | |
633 Address after_break_target_; | |
634 | |
635 // Per-thread data. | |
636 class ThreadLocal { | |
637 public: | |
638 // Top debugger entry. | |
639 base::AtomicWord current_debug_scope_; | |
640 | |
641 // Counter for generating next break id. | |
642 int break_count_; | |
643 | |
644 // Current break id. | |
645 int break_id_; | |
646 | |
647 // Frame id for the frame of the current break. | |
648 StackFrame::Id break_frame_id_; | |
649 | |
650 // Step action for last step performed. | |
651 StepAction last_step_action_; | |
652 | |
653 // Source statement position from last step next action. | |
654 int last_statement_position_; | |
655 | |
656 // Number of steps left to perform before debug event. | |
657 int step_count_; | |
658 | |
659 // Frame pointer from last step next or step frame action. | |
660 Address last_fp_; | |
661 | |
662 // Number of queued steps left to perform before debug event. | |
663 int queued_step_count_; | |
664 | |
665 // Frame pointer for frame from which step in was performed. | |
666 Address step_into_fp_; | |
667 | |
668 // Frame pointer for the frame where debugger should be called when current | |
669 // step out action is completed. | |
670 Address step_out_fp_; | |
671 | |
672 // Stores the way how LiveEdit has patched the stack. It is used when | |
673 // debugger returns control back to user script. | |
674 LiveEdit::FrameDropMode frame_drop_mode_; | |
675 | |
676 // When restarter frame is on stack, stores the address | |
677 // of the pointer to function being restarted. Otherwise (most of the time) | |
678 // stores NULL. This pointer is used with 'step in' implementation. | |
679 Object** restarter_frame_function_pointer_; | |
680 }; | |
681 | |
682 // Storage location for registers when handling debug break calls | |
683 ThreadLocal thread_local_; | |
684 | |
685 Isolate* isolate_; | |
686 | |
687 friend class Isolate; | |
688 friend class DebugScope; | |
689 friend class DisableBreak; | |
690 friend class LiveEdit; | |
691 friend class SuppressDebug; | |
692 | |
693 friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc | |
694 friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc | |
695 | |
696 DISALLOW_COPY_AND_ASSIGN(Debug); | |
697 }; | |
698 | |
699 | |
700 // This scope is used to load and enter the debug context and create a new | |
701 // break state. Leaving the scope will restore the previous state. | |
702 // On failure to load, FailedToEnter returns true. | |
703 class DebugScope BASE_EMBEDDED { | |
704 public: | |
705 explicit DebugScope(Debug* debug); | |
706 ~DebugScope(); | |
707 | |
708 // Check whether loading was successful. | |
709 inline bool failed() { return failed_; } | |
710 | |
711 // Get the active context from before entering the debugger. | |
712 inline Handle<Context> GetContext() { return save_.context(); } | |
713 | |
714 private: | |
715 Isolate* isolate() { return debug_->isolate_; } | |
716 | |
717 Debug* debug_; | |
718 DebugScope* prev_; // Previous scope if entered recursively. | |
719 StackFrame::Id break_frame_id_; // Previous break frame id. | |
720 int break_id_; // Previous break id. | |
721 bool failed_; // Did the debug context fail to load? | |
722 SaveContext save_; // Saves previous context. | |
723 PostponeInterruptsScope no_termination_exceptons_; | |
724 }; | |
725 | |
726 | |
727 // Stack allocated class for disabling break. | |
728 class DisableBreak BASE_EMBEDDED { | |
729 public: | |
730 explicit DisableBreak(Debug* debug, bool disable_break) | |
731 : debug_(debug), | |
732 previous_break_disabled_(debug->break_disabled_), | |
733 previous_in_debug_event_listener_(debug->in_debug_event_listener_) { | |
734 debug_->break_disabled_ = disable_break; | |
735 debug_->in_debug_event_listener_ = disable_break; | |
736 } | |
737 ~DisableBreak() { | |
738 debug_->break_disabled_ = previous_break_disabled_; | |
739 debug_->in_debug_event_listener_ = previous_in_debug_event_listener_; | |
740 } | |
741 | |
742 private: | |
743 Debug* debug_; | |
744 bool previous_break_disabled_; | |
745 bool previous_in_debug_event_listener_; | |
746 DISALLOW_COPY_AND_ASSIGN(DisableBreak); | |
747 }; | |
748 | |
749 | |
750 class SuppressDebug BASE_EMBEDDED { | |
751 public: | |
752 explicit SuppressDebug(Debug* debug) | |
753 : debug_(debug), old_state_(debug->is_suppressed_) { | |
754 debug_->is_suppressed_ = true; | |
755 } | |
756 ~SuppressDebug() { debug_->is_suppressed_ = old_state_; } | |
757 | |
758 private: | |
759 Debug* debug_; | |
760 bool old_state_; | |
761 DISALLOW_COPY_AND_ASSIGN(SuppressDebug); | |
762 }; | |
763 | |
764 | |
765 // Code generator routines. | |
766 class DebugCodegen : public AllStatic { | |
767 public: | |
768 enum DebugBreakCallHelperMode { | |
769 SAVE_RESULT_REGISTER, | |
770 IGNORE_RESULT_REGISTER | |
771 }; | |
772 | |
773 static void GenerateDebugBreakStub(MacroAssembler* masm, | |
774 DebugBreakCallHelperMode mode); | |
775 | |
776 static void GeneratePlainReturnLiveEdit(MacroAssembler* masm); | |
777 | |
778 // FrameDropper is a code replacement for a JavaScript frame with possibly | |
779 // several frames above. | |
780 // There is no calling conventions here, because it never actually gets | |
781 // called, it only gets returned to. | |
782 static void GenerateFrameDropperLiveEdit(MacroAssembler* masm); | |
783 | |
784 | |
785 static void GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode, | |
786 int call_argc = -1); | |
787 | |
788 static void PatchDebugBreakSlot(Address pc, Handle<Code> code); | |
789 static void ClearDebugBreakSlot(Address pc); | |
790 }; | |
791 | |
792 | |
793 } } // namespace v8::internal | |
794 | |
795 #endif // V8_DEBUG_H_ | |
OLD | NEW |