blob: 5a3722f166f5d7bd3fce31abd10caeab813fcfb8 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "api.h"
31#include "arguments.h"
32#include "bootstrapper.h"
33#include "code-stubs.h"
kasperl@chromium.org71affb52009-05-26 05:44:31 +000034#include "compilation-cache.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include "compiler.h"
36#include "debug.h"
37#include "execution.h"
38#include "global-handles.h"
ager@chromium.org65dad4b2009-04-23 08:48:43 +000039#include "ic.h"
40#include "ic-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000041#include "natives.h"
42#include "stub-cache.h"
kasper.lund7276f142008-07-30 08:49:36 +000043#include "log.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044
ager@chromium.org5ec48922009-05-05 07:25:34 +000045#include "../include/v8-debug.h"
46
kasperl@chromium.org71affb52009-05-26 05:44:31 +000047namespace v8 {
48namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000049
ager@chromium.org65dad4b2009-04-23 08:48:43 +000050#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051static void PrintLn(v8::Local<v8::Value> value) {
52 v8::Local<v8::String> s = value->ToString();
53 char* data = NewArray<char>(s->Length() + 1);
54 if (data == NULL) {
55 V8::FatalProcessOutOfMemory("PrintLn");
56 return;
57 }
58 s->WriteAscii(data);
59 PrintF("%s\n", data);
60 DeleteArray(data);
61}
62
63
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000064static Handle<Code> ComputeCallDebugBreak(int argc) {
65 CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc), Code);
66}
67
68
69static Handle<Code> ComputeCallDebugPrepareStepIn(int argc) {
70 CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugPrepareStepIn(argc), Code);
71}
72
73
74BreakLocationIterator::BreakLocationIterator(Handle<DebugInfo> debug_info,
75 BreakLocatorType type) {
76 debug_info_ = debug_info;
77 type_ = type;
78 reloc_iterator_ = NULL;
79 reloc_iterator_original_ = NULL;
80 Reset(); // Initialize the rest of the member variables.
81}
82
83
84BreakLocationIterator::~BreakLocationIterator() {
85 ASSERT(reloc_iterator_ != NULL);
86 ASSERT(reloc_iterator_original_ != NULL);
87 delete reloc_iterator_;
88 delete reloc_iterator_original_;
89}
90
91
92void BreakLocationIterator::Next() {
93 AssertNoAllocation nogc;
94 ASSERT(!RinfoDone());
95
96 // Iterate through reloc info for code and original code stopping at each
97 // breakable code target.
98 bool first = break_point_ == -1;
99 while (!RinfoDone()) {
100 if (!first) RinfoNext();
101 first = false;
102 if (RinfoDone()) return;
103
ager@chromium.org236ad962008-09-25 09:45:57 +0000104 // Whenever a statement position or (plain) position is passed update the
105 // current value of these.
106 if (RelocInfo::IsPosition(rmode())) {
107 if (RelocInfo::IsStatementPosition(rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108 statement_position_ =
109 rinfo()->data() - debug_info_->shared()->start_position();
110 }
ager@chromium.org236ad962008-09-25 09:45:57 +0000111 // Always update the position as we don't want that to be before the
112 // statement position.
113 position_ = rinfo()->data() - debug_info_->shared()->start_position();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114 ASSERT(position_ >= 0);
115 ASSERT(statement_position_ >= 0);
116 }
117
118 // Check for breakable code target. Look in the original code as setting
119 // break points can cause the code targets in the running (debugged) code to
120 // be of a different kind than in the original code.
ager@chromium.org236ad962008-09-25 09:45:57 +0000121 if (RelocInfo::IsCodeTarget(rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000122 Address target = original_rinfo()->target_address();
ager@chromium.org8bb60582008-12-11 12:02:20 +0000123 Code* code = Code::GetCodeFromTargetAddress(target);
ager@chromium.org236ad962008-09-25 09:45:57 +0000124 if (code->is_inline_cache_stub() || RelocInfo::IsConstructCall(rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000125 break_point_++;
126 return;
127 }
128 if (code->kind() == Code::STUB) {
129 if (type_ == ALL_BREAK_LOCATIONS) {
130 if (Debug::IsBreakStub(code)) {
131 break_point_++;
132 return;
133 }
134 } else {
135 ASSERT(type_ == SOURCE_BREAK_LOCATIONS);
136 if (Debug::IsSourceBreakStub(code)) {
137 break_point_++;
138 return;
139 }
140 }
141 }
142 }
143
144 // Check for break at return.
ager@chromium.org236ad962008-09-25 09:45:57 +0000145 if (RelocInfo::IsJSReturn(rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000146 // Set the positions to the end of the function.
147 if (debug_info_->shared()->HasSourceCode()) {
148 position_ = debug_info_->shared()->end_position() -
149 debug_info_->shared()->start_position();
150 } else {
151 position_ = 0;
152 }
153 statement_position_ = position_;
154 break_point_++;
155 return;
156 }
157 }
158}
159
160
161void BreakLocationIterator::Next(int count) {
162 while (count > 0) {
163 Next();
164 count--;
165 }
166}
167
168
169// Find the break point closest to the supplied address.
170void BreakLocationIterator::FindBreakLocationFromAddress(Address pc) {
171 // Run through all break points to locate the one closest to the address.
172 int closest_break_point = 0;
173 int distance = kMaxInt;
174 while (!Done()) {
175 // Check if this break point is closer that what was previously found.
176 if (this->pc() < pc && pc - this->pc() < distance) {
177 closest_break_point = break_point();
178 distance = pc - this->pc();
179 // Check whether we can't get any closer.
180 if (distance == 0) break;
181 }
182 Next();
183 }
184
185 // Move to the break point found.
186 Reset();
187 Next(closest_break_point);
188}
189
190
191// Find the break point closest to the supplied source position.
192void BreakLocationIterator::FindBreakLocationFromPosition(int position) {
193 // Run through all break points to locate the one closest to the source
194 // position.
195 int closest_break_point = 0;
196 int distance = kMaxInt;
197 while (!Done()) {
198 // Check if this break point is closer that what was previously found.
199 if (position <= statement_position() &&
200 statement_position() - position < distance) {
201 closest_break_point = break_point();
202 distance = statement_position() - position;
203 // Check whether we can't get any closer.
204 if (distance == 0) break;
205 }
206 Next();
207 }
208
209 // Move to the break point found.
210 Reset();
211 Next(closest_break_point);
212}
213
214
215void BreakLocationIterator::Reset() {
216 // Create relocation iterators for the two code objects.
217 if (reloc_iterator_ != NULL) delete reloc_iterator_;
218 if (reloc_iterator_original_ != NULL) delete reloc_iterator_original_;
219 reloc_iterator_ = new RelocIterator(debug_info_->code());
220 reloc_iterator_original_ = new RelocIterator(debug_info_->original_code());
221
222 // Position at the first break point.
223 break_point_ = -1;
224 position_ = 1;
225 statement_position_ = 1;
226 Next();
227}
228
229
230bool BreakLocationIterator::Done() const {
231 return RinfoDone();
232}
233
234
235void BreakLocationIterator::SetBreakPoint(Handle<Object> break_point_object) {
236 // If there is not already a real break point here patch code with debug
237 // break.
238 if (!HasBreakPoint()) {
239 SetDebugBreak();
240 }
241 ASSERT(IsDebugBreak());
242 // Set the break point information.
243 DebugInfo::SetBreakPoint(debug_info_, code_position(),
244 position(), statement_position(),
245 break_point_object);
246}
247
248
249void BreakLocationIterator::ClearBreakPoint(Handle<Object> break_point_object) {
250 // Clear the break point information.
251 DebugInfo::ClearBreakPoint(debug_info_, code_position(), break_point_object);
252 // If there are no more break points here remove the debug break.
253 if (!HasBreakPoint()) {
254 ClearDebugBreak();
255 ASSERT(!IsDebugBreak());
256 }
257}
258
259
260void BreakLocationIterator::SetOneShot() {
261 // If there is a real break point here no more to do.
262 if (HasBreakPoint()) {
263 ASSERT(IsDebugBreak());
264 return;
265 }
266
267 // Patch code with debug break.
268 SetDebugBreak();
269}
270
271
272void BreakLocationIterator::ClearOneShot() {
273 // If there is a real break point here no more to do.
274 if (HasBreakPoint()) {
275 ASSERT(IsDebugBreak());
276 return;
277 }
278
279 // Patch code removing debug break.
280 ClearDebugBreak();
281 ASSERT(!IsDebugBreak());
282}
283
284
285void BreakLocationIterator::SetDebugBreak() {
286 // If there is already a break point here just return. This might happen if
v8.team.kasperl727e9952008-09-02 14:56:44 +0000287 // the same code is flooded with break points twice. Flooding the same
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 // function twice might happen when stepping in a function with an exception
289 // handler as the handler and the function is the same.
290 if (IsDebugBreak()) {
291 return;
292 }
293
ager@chromium.org236ad962008-09-25 09:45:57 +0000294 if (RelocInfo::IsJSReturn(rmode())) {
iposva@chromium.org245aa852009-02-10 00:49:54 +0000295 // Patch the frame exit code with a break point.
296 SetDebugBreakAtReturn();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000298 // Patch the IC call.
299 SetDebugBreakAtIC();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300 }
301 ASSERT(IsDebugBreak());
302}
303
304
305void BreakLocationIterator::ClearDebugBreak() {
ager@chromium.org236ad962008-09-25 09:45:57 +0000306 if (RelocInfo::IsJSReturn(rmode())) {
iposva@chromium.org245aa852009-02-10 00:49:54 +0000307 // Restore the frame exit code.
308 ClearDebugBreakAtReturn();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 } else {
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000310 // Patch the IC call.
311 ClearDebugBreakAtIC();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 ASSERT(!IsDebugBreak());
314}
315
316
317void BreakLocationIterator::PrepareStepIn() {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000318 HandleScope scope;
319
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 // Step in can only be prepared if currently positioned on an IC call or
321 // construct call.
322 Address target = rinfo()->target_address();
ager@chromium.org8bb60582008-12-11 12:02:20 +0000323 Code* code = Code::GetCodeFromTargetAddress(target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324 if (code->is_call_stub()) {
325 // Step in through IC call is handled by the runtime system. Therefore make
326 // sure that the any current IC is cleared and the runtime system is
327 // called. If the executing code has a debug break at the location change
328 // the call in the original code as it is the code there that will be
329 // executed in place of the debug break call.
330 Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count());
331 if (IsDebugBreak()) {
332 original_rinfo()->set_target_address(stub->entry());
333 } else {
334 rinfo()->set_target_address(stub->entry());
335 }
336 } else {
v8.team.kasperl727e9952008-09-02 14:56:44 +0000337 // Step in through constructs call requires no changes to the running code.
ager@chromium.org236ad962008-09-25 09:45:57 +0000338 ASSERT(RelocInfo::IsConstructCall(rmode()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000339 }
340}
341
342
343// Check whether the break point is at a position which will exit the function.
344bool BreakLocationIterator::IsExit() const {
ager@chromium.org236ad962008-09-25 09:45:57 +0000345 return (RelocInfo::IsJSReturn(rmode()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346}
347
348
349bool BreakLocationIterator::HasBreakPoint() {
350 return debug_info_->HasBreakPoint(code_position());
351}
352
353
354// Check whether there is a debug break at the current position.
355bool BreakLocationIterator::IsDebugBreak() {
ager@chromium.org236ad962008-09-25 09:45:57 +0000356 if (RelocInfo::IsJSReturn(rmode())) {
iposva@chromium.org245aa852009-02-10 00:49:54 +0000357 return IsDebugBreakAtReturn();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358 } else {
359 return Debug::IsDebugBreak(rinfo()->target_address());
360 }
361}
362
363
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000364void BreakLocationIterator::SetDebugBreakAtIC() {
365 // Patch the original code with the current address as the current address
366 // might have changed by the inline caching since the code was copied.
367 original_rinfo()->set_target_address(rinfo()->target_address());
368
369 RelocInfo::Mode mode = rmode();
370 if (RelocInfo::IsCodeTarget(mode)) {
371 Address target = rinfo()->target_address();
372 Handle<Code> code(Code::GetCodeFromTargetAddress(target));
373
374 // Patch the code to invoke the builtin debug break function matching the
375 // calling convention used by the call site.
376 Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode));
377 rinfo()->set_target_address(dbgbrk_code->entry());
378
379 // For stubs that refer back to an inlined version clear the cached map for
380 // the inlined case to always go through the IC. As long as the break point
381 // is set the patching performed by the runtime system will take place in
382 // the code copy and will therefore have no effect on the running code
383 // keeping it from using the inlined code.
ager@chromium.org5ec48922009-05-05 07:25:34 +0000384 if (code->is_keyed_load_stub()) KeyedLoadIC::ClearInlinedVersion(pc());
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000385 }
386}
387
388
389void BreakLocationIterator::ClearDebugBreakAtIC() {
390 // Patch the code to the original invoke.
391 rinfo()->set_target_address(original_rinfo()->target_address());
392}
393
394
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395Object* BreakLocationIterator::BreakPointObjects() {
396 return debug_info_->GetBreakPointObjects(code_position());
397}
398
399
ager@chromium.org381abbb2009-02-25 13:23:22 +0000400// Clear out all the debug break code. This is ONLY supposed to be used when
401// shutting down the debugger as it will leave the break point information in
402// DebugInfo even though the code is patched back to the non break point state.
403void BreakLocationIterator::ClearAllDebugBreak() {
404 while (!Done()) {
405 ClearDebugBreak();
406 Next();
407 }
408}
409
410
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000411bool BreakLocationIterator::RinfoDone() const {
412 ASSERT(reloc_iterator_->done() == reloc_iterator_original_->done());
413 return reloc_iterator_->done();
414}
415
416
417void BreakLocationIterator::RinfoNext() {
418 reloc_iterator_->next();
419 reloc_iterator_original_->next();
420#ifdef DEBUG
421 ASSERT(reloc_iterator_->done() == reloc_iterator_original_->done());
422 if (!reloc_iterator_->done()) {
423 ASSERT(rmode() == original_rmode());
424 }
425#endif
426}
427
428
429bool Debug::has_break_points_ = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000430ScriptCache* Debug::script_cache_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000431DebugInfoListNode* Debug::debug_info_list_ = NULL;
432
433
434// Threading support.
435void Debug::ThreadInit() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000436 thread_local_.break_count_ = 0;
437 thread_local_.break_id_ = 0;
438 thread_local_.break_frame_id_ = StackFrame::NO_ID;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000439 thread_local_.last_step_action_ = StepNone;
ager@chromium.org236ad962008-09-25 09:45:57 +0000440 thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000441 thread_local_.step_count_ = 0;
442 thread_local_.last_fp_ = 0;
443 thread_local_.step_into_fp_ = 0;
444 thread_local_.after_break_target_ = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000445 thread_local_.debugger_entry_ = NULL;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000446 thread_local_.pending_interrupts_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000447}
448
449
450JSCallerSavedBuffer Debug::registers_;
451Debug::ThreadLocal Debug::thread_local_;
452
453
454char* Debug::ArchiveDebug(char* storage) {
455 char* to = storage;
456 memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
457 to += sizeof(ThreadLocal);
458 memcpy(to, reinterpret_cast<char*>(&registers_), sizeof(registers_));
459 ThreadInit();
460 ASSERT(to <= storage + ArchiveSpacePerThread());
461 return storage + ArchiveSpacePerThread();
462}
463
464
465char* Debug::RestoreDebug(char* storage) {
466 char* from = storage;
467 memcpy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
468 from += sizeof(ThreadLocal);
469 memcpy(reinterpret_cast<char*>(&registers_), from, sizeof(registers_));
470 ASSERT(from <= storage + ArchiveSpacePerThread());
471 return storage + ArchiveSpacePerThread();
472}
473
474
475int Debug::ArchiveSpacePerThread() {
476 return sizeof(ThreadLocal) + sizeof(registers_);
477}
478
479
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000480// Default break enabled.
481bool Debug::disable_break_ = false;
482
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000483// Default call debugger on uncaught exception.
484bool Debug::break_on_exception_ = false;
485bool Debug::break_on_uncaught_exception_ = true;
486
487Handle<Context> Debug::debug_context_ = Handle<Context>();
488Code* Debug::debug_break_return_entry_ = NULL;
489Code* Debug::debug_break_return_ = NULL;
490
491
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000492void ScriptCache::Add(Handle<Script> script) {
493 // Create an entry in the hash map for the script.
494 int id = Smi::cast(script->id())->value();
495 HashMap::Entry* entry =
496 HashMap::Lookup(reinterpret_cast<void*>(id), Hash(id), true);
497 if (entry->value != NULL) {
498 ASSERT(*script == *reinterpret_cast<Script**>(entry->value));
499 return;
500 }
501
502 // Globalize the script object, make it weak and use the location of the
503 // global handle as the value in the hash map.
504 Handle<Script> script_ =
505 Handle<Script>::cast((GlobalHandles::Create(*script)));
506 GlobalHandles::MakeWeak(reinterpret_cast<Object**>(script_.location()),
507 this, ScriptCache::HandleWeakScript);
508 entry->value = script_.location();
509}
510
511
512Handle<FixedArray> ScriptCache::GetScripts() {
513 Handle<FixedArray> instances = Factory::NewFixedArray(occupancy());
514 int count = 0;
515 for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) {
516 ASSERT(entry->value != NULL);
517 if (entry->value != NULL) {
518 instances->set(count, *reinterpret_cast<Script**>(entry->value));
519 count++;
520 }
521 }
522 return instances;
523}
524
525
526void ScriptCache::ProcessCollectedScripts() {
527 for (int i = 0; i < collected_scripts_.length(); i++) {
528 Debugger::OnScriptCollected(collected_scripts_[i]);
529 }
530 collected_scripts_.Clear();
531}
532
533
534void ScriptCache::Clear() {
535 // Iterate the script cache to get rid of all the weak handles.
536 for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) {
537 ASSERT(entry != NULL);
538 Object** location = reinterpret_cast<Object**>(entry->value);
539 ASSERT((*location)->IsScript());
540 GlobalHandles::ClearWeakness(location);
541 GlobalHandles::Destroy(location);
542 }
543 // Clear the content of the hash map.
544 HashMap::Clear();
545}
546
547
548void ScriptCache::HandleWeakScript(v8::Persistent<v8::Value> obj, void* data) {
549 ScriptCache* script_cache = reinterpret_cast<ScriptCache*>(data);
550 // Find the location of the global handle.
551 Script** location =
552 reinterpret_cast<Script**>(Utils::OpenHandle(*obj).location());
553 ASSERT((*location)->IsScript());
554
555 // Remove the entry from the cache.
556 int id = Smi::cast((*location)->id())->value();
557 script_cache->Remove(reinterpret_cast<void*>(id), Hash(id));
558 script_cache->collected_scripts_.Add(id);
559
560 // Clear the weak handle.
561 obj.Dispose();
562 obj.Clear();
563}
564
565
566void Debug::Setup(bool create_heap_objects) {
567 ThreadInit();
568 if (create_heap_objects) {
569 // Get code to handle entry to debug break on return.
570 debug_break_return_entry_ =
571 Builtins::builtin(Builtins::Return_DebugBreakEntry);
572 ASSERT(debug_break_return_entry_->IsCode());
573
574 // Get code to handle debug break on return.
575 debug_break_return_ =
576 Builtins::builtin(Builtins::Return_DebugBreak);
577 ASSERT(debug_break_return_->IsCode());
578 }
579}
580
581
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000582void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000583 DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data);
584 RemoveDebugInfo(node->debug_info());
585#ifdef DEBUG
586 node = Debug::debug_info_list_;
587 while (node != NULL) {
588 ASSERT(node != reinterpret_cast<DebugInfoListNode*>(data));
589 node = node->next();
590 }
591#endif
592}
593
594
595DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
596 // Globalize the request debug info object and make it weak.
597 debug_info_ = Handle<DebugInfo>::cast((GlobalHandles::Create(debug_info)));
598 GlobalHandles::MakeWeak(reinterpret_cast<Object**>(debug_info_.location()),
599 this, Debug::HandleWeakDebugInfo);
600}
601
602
603DebugInfoListNode::~DebugInfoListNode() {
604 GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_.location()));
605}
606
607
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000608bool Debug::CompileDebuggerScript(int index) {
609 HandleScope scope;
610
kasper.lund44510672008-07-25 07:37:58 +0000611 // Bail out if the index is invalid.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 if (index == -1) {
613 return false;
614 }
kasper.lund44510672008-07-25 07:37:58 +0000615
616 // Find source and name for the requested script.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000617 Handle<String> source_code = Bootstrapper::NativesSourceLookup(index);
618 Vector<const char> name = Natives::GetScriptName(index);
619 Handle<String> script_name = Factory::NewStringFromAscii(name);
620
621 // Compile the script.
622 bool allow_natives_syntax = FLAG_allow_natives_syntax;
623 FLAG_allow_natives_syntax = true;
624 Handle<JSFunction> boilerplate;
625 boilerplate = Compiler::Compile(source_code, script_name, 0, 0, NULL, NULL);
626 FLAG_allow_natives_syntax = allow_natives_syntax;
627
628 // Silently ignore stack overflows during compilation.
629 if (boilerplate.is_null()) {
630 ASSERT(Top::has_pending_exception());
631 Top::clear_pending_exception();
632 return false;
633 }
634
kasper.lund44510672008-07-25 07:37:58 +0000635 // Execute the boilerplate function in the debugger context.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000636 Handle<Context> context = Top::global_context();
kasper.lund44510672008-07-25 07:37:58 +0000637 bool caught_exception = false;
638 Handle<JSFunction> function =
639 Factory::NewFunctionFromBoilerplate(boilerplate, context);
640 Handle<Object> result =
641 Execution::TryCall(function, Handle<Object>(context->global()),
642 0, NULL, &caught_exception);
643
644 // Check for caught exceptions.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000645 if (caught_exception) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000646 Handle<Object> message = MessageHandler::MakeMessageObject(
647 "error_loading_debugger", NULL, HandleVector<Object>(&result, 1),
648 Handle<String>());
649 MessageHandler::ReportMessage(NULL, message);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000650 return false;
651 }
652
kasper.lund44510672008-07-25 07:37:58 +0000653 // Mark this script as native and return successfully.
654 Handle<Script> script(Script::cast(function->shared()->script()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000655 script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return true;
657}
658
659
660bool Debug::Load() {
661 // Return if debugger is already loaded.
kasper.lund212ac232008-07-16 07:07:30 +0000662 if (IsLoaded()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663
kasper.lund44510672008-07-25 07:37:58 +0000664 // Bail out if we're already in the process of compiling the native
665 // JavaScript source code for the debugger.
mads.s.agercbaa0602008-08-14 13:41:48 +0000666 if (Debugger::compiling_natives() || Debugger::is_loading_debugger())
667 return false;
668 Debugger::set_loading_debugger(true);
kasper.lund44510672008-07-25 07:37:58 +0000669
670 // Disable breakpoints and interrupts while compiling and running the
671 // debugger scripts including the context creation code.
672 DisableBreak disable(true);
673 PostponeInterruptsScope postpone;
674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000675 // Create the debugger context.
676 HandleScope scope;
kasper.lund44510672008-07-25 07:37:58 +0000677 Handle<Context> context =
678 Bootstrapper::CreateEnvironment(Handle<Object>::null(),
679 v8::Handle<ObjectTemplate>(),
680 NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000681
kasper.lund44510672008-07-25 07:37:58 +0000682 // Use the debugger context.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000683 SaveContext save;
kasper.lund44510672008-07-25 07:37:58 +0000684 Top::set_context(*context);
kasper.lund44510672008-07-25 07:37:58 +0000685
686 // Expose the builtins object in the debugger context.
687 Handle<String> key = Factory::LookupAsciiSymbol("builtins");
688 Handle<GlobalObject> global = Handle<GlobalObject>(context->global());
689 SetProperty(global, key, Handle<Object>(global->builtins()), NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000690
691 // Compile the JavaScript for the debugger in the debugger context.
692 Debugger::set_compiling_natives(true);
kasper.lund44510672008-07-25 07:37:58 +0000693 bool caught_exception =
694 !CompileDebuggerScript(Natives::GetIndex("mirror")) ||
695 !CompileDebuggerScript(Natives::GetIndex("debug"));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 Debugger::set_compiling_natives(false);
697
mads.s.agercbaa0602008-08-14 13:41:48 +0000698 // Make sure we mark the debugger as not loading before we might
699 // return.
700 Debugger::set_loading_debugger(false);
701
kasper.lund44510672008-07-25 07:37:58 +0000702 // Check for caught exceptions.
703 if (caught_exception) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704
705 // Debugger loaded.
kasper.lund44510672008-07-25 07:37:58 +0000706 debug_context_ = Handle<Context>::cast(GlobalHandles::Create(*context));
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000708 return true;
709}
710
711
712void Debug::Unload() {
713 // Return debugger is not loaded.
714 if (!IsLoaded()) {
715 return;
716 }
717
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000718 // Clear the script cache.
719 DestroyScriptCache();
720
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721 // Clear debugger context global handle.
722 GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_context_.location()));
723 debug_context_ = Handle<Context>();
724}
725
726
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000727// Set the flag indicating that preemption happened during debugging.
728void Debug::PreemptionWhileInDebugger() {
729 ASSERT(InDebugger());
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000730 Debug::set_interrupts_pending(PREEMPT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000731}
732
733
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000734void Debug::Iterate(ObjectVisitor* v) {
iposva@chromium.org245aa852009-02-10 00:49:54 +0000735 v->VisitPointer(bit_cast<Object**, Code**>(&(debug_break_return_entry_)));
736 v->VisitPointer(bit_cast<Object**, Code**>(&(debug_break_return_)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737}
738
739
740Object* Debug::Break(Arguments args) {
741 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +0000742 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000743
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000744 // Get the top-most JavaScript frame.
745 JavaScriptFrameIterator it;
746 JavaScriptFrame* frame = it.frame();
747
748 // Just continue if breaks are disabled or debugger cannot be loaded.
749 if (disable_break() || !Load()) {
750 SetAfterBreakTarget(frame);
mads.s.ager31e71382008-08-13 09:32:07 +0000751 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752 }
753
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000754 // Enter the debugger.
755 EnterDebugger debugger;
756 if (debugger.FailedToEnter()) {
757 return Heap::undefined_value();
758 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759
kasper.lund44510672008-07-25 07:37:58 +0000760 // Postpone interrupt during breakpoint processing.
761 PostponeInterruptsScope postpone;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000762
763 // Get the debug info (create it if it does not exist).
764 Handle<SharedFunctionInfo> shared =
765 Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
766 Handle<DebugInfo> debug_info = GetDebugInfo(shared);
767
768 // Find the break point where execution has stopped.
769 BreakLocationIterator break_location_iterator(debug_info,
770 ALL_BREAK_LOCATIONS);
771 break_location_iterator.FindBreakLocationFromAddress(frame->pc());
772
773 // Check whether step next reached a new statement.
774 if (!StepNextContinue(&break_location_iterator, frame)) {
775 // Decrease steps left if performing multiple steps.
776 if (thread_local_.step_count_ > 0) {
777 thread_local_.step_count_--;
778 }
779 }
780
781 // If there is one or more real break points check whether any of these are
782 // triggered.
783 Handle<Object> break_points_hit(Heap::undefined_value());
784 if (break_location_iterator.HasBreakPoint()) {
785 Handle<Object> break_point_objects =
786 Handle<Object>(break_location_iterator.BreakPointObjects());
787 break_points_hit = CheckBreakPoints(break_point_objects);
788 }
789
790 // Notify debugger if a real break point is triggered or if performing single
791 // stepping with no more steps to perform. Otherwise do another step.
792 if (!break_points_hit->IsUndefined() ||
793 (thread_local_.last_step_action_ != StepNone &&
794 thread_local_.step_count_ == 0)) {
795 // Clear all current stepping setup.
796 ClearStepping();
797
798 // Notify the debug event listeners.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000799 Debugger::OnDebugBreak(break_points_hit, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 } else if (thread_local_.last_step_action_ != StepNone) {
801 // Hold on to last step action as it is cleared by the call to
802 // ClearStepping.
803 StepAction step_action = thread_local_.last_step_action_;
804 int step_count = thread_local_.step_count_;
805
806 // Clear all current stepping setup.
807 ClearStepping();
808
809 // Set up for the remaining steps.
810 PrepareStep(step_action, step_count);
811 }
812
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000813 // Install jump to the call address which was overwritten.
814 SetAfterBreakTarget(frame);
815
mads.s.ager31e71382008-08-13 09:32:07 +0000816 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000817}
818
819
820// Check the break point objects for whether one or more are actually
821// triggered. This function returns a JSArray with the break point objects
822// which is triggered.
823Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) {
824 int break_points_hit_count = 0;
825 Handle<JSArray> break_points_hit = Factory::NewJSArray(1);
826
v8.team.kasperl727e9952008-09-02 14:56:44 +0000827 // If there are multiple break points they are in a FixedArray.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000828 ASSERT(!break_point_objects->IsUndefined());
829 if (break_point_objects->IsFixedArray()) {
830 Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
831 for (int i = 0; i < array->length(); i++) {
832 Handle<Object> o(array->get(i));
833 if (CheckBreakPoint(o)) {
834 break_points_hit->SetElement(break_points_hit_count++, *o);
835 }
836 }
837 } else {
838 if (CheckBreakPoint(break_point_objects)) {
839 break_points_hit->SetElement(break_points_hit_count++,
840 *break_point_objects);
841 }
842 }
843
844 // Return undefined if no break points where triggered.
845 if (break_points_hit_count == 0) {
846 return Factory::undefined_value();
847 }
848 return break_points_hit;
849}
850
851
852// Check whether a single break point object is triggered.
853bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000854 HandleScope scope;
855
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000856 // Ignore check if break point object is not a JSObject.
857 if (!break_point_object->IsJSObject()) return true;
858
859 // Get the function CheckBreakPoint (defined in debug.js).
860 Handle<JSFunction> check_break_point =
861 Handle<JSFunction>(JSFunction::cast(
862 debug_context()->global()->GetProperty(
863 *Factory::LookupAsciiSymbol("IsBreakPointTriggered"))));
864
865 // Get the break id as an object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000866 Handle<Object> break_id = Factory::NewNumberFromInt(Debug::break_id());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000867
868 // Call HandleBreakPointx.
869 bool caught_exception = false;
870 const int argc = 2;
871 Object** argv[argc] = {
872 break_id.location(),
873 reinterpret_cast<Object**>(break_point_object.location())
874 };
875 Handle<Object> result = Execution::TryCall(check_break_point,
876 Top::builtins(), argc, argv,
877 &caught_exception);
878
879 // If exception or non boolean result handle as not triggered
880 if (caught_exception || !result->IsBoolean()) {
881 return false;
882 }
883
884 // Return whether the break point is triggered.
885 return *result == Heap::true_value();
886}
887
888
889// Check whether the function has debug information.
890bool Debug::HasDebugInfo(Handle<SharedFunctionInfo> shared) {
891 return !shared->debug_info()->IsUndefined();
892}
893
894
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000895// Return the debug info for this function. EnsureDebugInfo must be called
896// prior to ensure the debug info has been generated for shared.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897Handle<DebugInfo> Debug::GetDebugInfo(Handle<SharedFunctionInfo> shared) {
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000898 ASSERT(HasDebugInfo(shared));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 return Handle<DebugInfo>(DebugInfo::cast(shared->debug_info()));
900}
901
902
903void Debug::SetBreakPoint(Handle<SharedFunctionInfo> shared,
904 int source_position,
905 Handle<Object> break_point_object) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000906 HandleScope scope;
907
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000908 if (!EnsureDebugInfo(shared)) {
909 // Return if retrieving debug info failed.
910 return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000911 }
912
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000913 Handle<DebugInfo> debug_info = GetDebugInfo(shared);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914 // Source positions starts with zero.
915 ASSERT(source_position >= 0);
916
917 // Find the break point and change it.
918 BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
919 it.FindBreakLocationFromPosition(source_position);
920 it.SetBreakPoint(break_point_object);
921
922 // At least one active break point now.
923 ASSERT(debug_info->GetBreakPointCount() > 0);
924}
925
926
927void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000928 HandleScope scope;
929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000930 DebugInfoListNode* node = debug_info_list_;
931 while (node != NULL) {
932 Object* result = DebugInfo::FindBreakPointInfo(node->debug_info(),
933 break_point_object);
934 if (!result->IsUndefined()) {
935 // Get information in the break point.
936 BreakPointInfo* break_point_info = BreakPointInfo::cast(result);
937 Handle<DebugInfo> debug_info = node->debug_info();
938 Handle<SharedFunctionInfo> shared(debug_info->shared());
939 int source_position = break_point_info->statement_position()->value();
940
941 // Source positions starts with zero.
942 ASSERT(source_position >= 0);
943
944 // Find the break point and clear it.
945 BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
946 it.FindBreakLocationFromPosition(source_position);
947 it.ClearBreakPoint(break_point_object);
948
949 // If there are no more break points left remove the debug info for this
950 // function.
951 if (debug_info->GetBreakPointCount() == 0) {
952 RemoveDebugInfo(debug_info);
953 }
954
955 return;
956 }
957 node = node->next();
958 }
959}
960
961
ager@chromium.org381abbb2009-02-25 13:23:22 +0000962void Debug::ClearAllBreakPoints() {
963 DebugInfoListNode* node = debug_info_list_;
964 while (node != NULL) {
965 // Remove all debug break code.
966 BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
967 it.ClearAllDebugBreak();
968 node = node->next();
969 }
970
971 // Remove all debug info.
972 while (debug_info_list_ != NULL) {
973 RemoveDebugInfo(debug_info_list_->debug_info());
974 }
975}
976
977
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000978void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000979 // Make sure the function has setup the debug info.
980 if (!EnsureDebugInfo(shared)) {
981 // Return if we failed to retrieve the debug info.
982 return;
983 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000984
985 // Flood the function with break points.
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000986 BreakLocationIterator it(GetDebugInfo(shared), ALL_BREAK_LOCATIONS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 while (!it.Done()) {
988 it.SetOneShot();
989 it.Next();
990 }
991}
992
993
994void Debug::FloodHandlerWithOneShot() {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000995 // Iterate through the JavaScript stack looking for handlers.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000996 StackFrame::Id id = break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +0000997 if (id == StackFrame::NO_ID) {
998 // If there is no JavaScript stack don't do anything.
999 return;
1000 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) {
1002 JavaScriptFrame* frame = it.frame();
1003 if (frame->HasHandler()) {
1004 Handle<SharedFunctionInfo> shared =
1005 Handle<SharedFunctionInfo>(
1006 JSFunction::cast(frame->function())->shared());
1007 // Flood the function with the catch block with break points
1008 FloodWithOneShot(shared);
1009 return;
1010 }
1011 }
1012}
1013
1014
1015void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
1016 if (type == BreakUncaughtException) {
1017 break_on_uncaught_exception_ = enable;
1018 } else {
1019 break_on_exception_ = enable;
1020 }
1021}
1022
1023
1024void Debug::PrepareStep(StepAction step_action, int step_count) {
1025 HandleScope scope;
1026 ASSERT(Debug::InDebugger());
1027
1028 // Remember this step action and count.
1029 thread_local_.last_step_action_ = step_action;
1030 thread_local_.step_count_ = step_count;
1031
1032 // Get the frame where the execution has stopped and skip the debug frame if
1033 // any. The debug frame will only be present if execution was stopped due to
1034 // hitting a break point. In other situations (e.g. unhandled exception) the
1035 // debug frame is not present.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001036 StackFrame::Id id = break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00001037 if (id == StackFrame::NO_ID) {
1038 // If there is no JavaScript stack don't do anything.
1039 return;
1040 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001041 JavaScriptFrameIterator frames_it(id);
1042 JavaScriptFrame* frame = frames_it.frame();
1043
1044 // First of all ensure there is one-shot break points in the top handler
1045 // if any.
1046 FloodHandlerWithOneShot();
1047
1048 // If the function on the top frame is unresolved perform step out. This will
1049 // be the case when calling unknown functions and having the debugger stopped
1050 // in an unhandled exception.
1051 if (!frame->function()->IsJSFunction()) {
1052 // Step out: Find the calling JavaScript frame and flood it with
1053 // breakpoints.
1054 frames_it.Advance();
1055 // Fill the function to return to with one-shot break points.
1056 JSFunction* function = JSFunction::cast(frames_it.frame()->function());
1057 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
1058 return;
1059 }
1060
1061 // Get the debug info (create it if it does not exist).
1062 Handle<SharedFunctionInfo> shared =
1063 Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001064 if (!EnsureDebugInfo(shared)) {
1065 // Return if ensuring debug info failed.
1066 return;
1067 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001068 Handle<DebugInfo> debug_info = GetDebugInfo(shared);
1069
1070 // Find the break location where execution has stopped.
1071 BreakLocationIterator it(debug_info, ALL_BREAK_LOCATIONS);
1072 it.FindBreakLocationFromAddress(frame->pc());
1073
1074 // Compute whether or not the target is a call target.
1075 bool is_call_target = false;
ager@chromium.org236ad962008-09-25 09:45:57 +00001076 if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001077 Address target = it.rinfo()->target_address();
ager@chromium.org8bb60582008-12-11 12:02:20 +00001078 Code* code = Code::GetCodeFromTargetAddress(target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079 if (code->is_call_stub()) is_call_target = true;
1080 }
1081
v8.team.kasperl727e9952008-09-02 14:56:44 +00001082 // If this is the last break code target step out is the only possibility.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001083 if (it.IsExit() || step_action == StepOut) {
1084 // Step out: If there is a JavaScript caller frame, we need to
1085 // flood it with breakpoints.
1086 frames_it.Advance();
1087 if (!frames_it.done()) {
1088 // Fill the function to return to with one-shot break points.
1089 JSFunction* function = JSFunction::cast(frames_it.frame()->function());
1090 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
1091 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001092 } else if (!(is_call_target || RelocInfo::IsConstructCall(it.rmode())) ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001093 step_action == StepNext || step_action == StepMin) {
1094 // Step next or step min.
1095
1096 // Fill the current function with one-shot break points.
1097 FloodWithOneShot(shared);
1098
1099 // Remember source position and frame to handle step next.
1100 thread_local_.last_statement_position_ =
1101 debug_info->code()->SourceStatementPosition(frame->pc());
1102 thread_local_.last_fp_ = frame->fp();
1103 } else {
1104 // Fill the current function with one-shot break points even for step in on
1105 // a call target as the function called might be a native function for
1106 // which step in will not stop.
1107 FloodWithOneShot(shared);
1108
1109 // Step in or Step in min
1110 it.PrepareStepIn();
1111 ActivateStepIn(frame);
1112 }
1113}
1114
1115
1116// Check whether the current debug break should be reported to the debugger. It
1117// is used to have step next and step in only report break back to the debugger
1118// if on a different frame or in a different statement. In some situations
1119// there will be several break points in the same statement when the code is
v8.team.kasperl727e9952008-09-02 14:56:44 +00001120// flooded with one-shot break points. This function helps to perform several
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121// steps before reporting break back to the debugger.
1122bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
1123 JavaScriptFrame* frame) {
1124 // If the step last action was step next or step in make sure that a new
1125 // statement is hit.
1126 if (thread_local_.last_step_action_ == StepNext ||
1127 thread_local_.last_step_action_ == StepIn) {
1128 // Never continue if returning from function.
1129 if (break_location_iterator->IsExit()) return false;
1130
1131 // Continue if we are still on the same frame and in the same statement.
1132 int current_statement_position =
1133 break_location_iterator->code()->SourceStatementPosition(frame->pc());
1134 return thread_local_.last_fp_ == frame->fp() &&
ager@chromium.org236ad962008-09-25 09:45:57 +00001135 thread_local_.last_statement_position_ == current_statement_position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137
1138 // No step next action - don't continue.
1139 return false;
1140}
1141
1142
1143// Check whether the code object at the specified address is a debug break code
1144// object.
1145bool Debug::IsDebugBreak(Address addr) {
ager@chromium.org8bb60582008-12-11 12:02:20 +00001146 Code* code = Code::GetCodeFromTargetAddress(addr);
kasper.lund7276f142008-07-30 08:49:36 +00001147 return code->ic_state() == DEBUG_BREAK;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001148}
1149
1150
1151// Check whether a code stub with the specified major key is a possible break
1152// point location when looking for source break locations.
1153bool Debug::IsSourceBreakStub(Code* code) {
1154 CodeStub::Major major_key = code->major_key();
1155 return major_key == CodeStub::CallFunction;
1156}
1157
1158
1159// Check whether a code stub with the specified major key is a possible break
1160// location.
1161bool Debug::IsBreakStub(Code* code) {
1162 CodeStub::Major major_key = code->major_key();
1163 return major_key == CodeStub::CallFunction ||
1164 major_key == CodeStub::StackCheck;
1165}
1166
1167
1168// Find the builtin to use for invoking the debug break
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001169Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001170 // Find the builtin debug break function matching the calling convention
1171 // used by the call site.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001172 if (code->is_inline_cache_stub()) {
1173 if (code->is_call_stub()) {
1174 return ComputeCallDebugBreak(code->arguments_count());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001175 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001176 if (code->is_load_stub()) {
1177 return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
1178 }
1179 if (code->is_store_stub()) {
1180 return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
1181 }
1182 if (code->is_keyed_load_stub()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001183 Handle<Code> result =
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001184 Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185 return result;
1186 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001187 if (code->is_keyed_store_stub()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001188 Handle<Code> result =
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001189 Handle<Code>(Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 return result;
1191 }
1192 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001193 if (RelocInfo::IsConstructCall(mode)) {
1194 Handle<Code> result =
1195 Handle<Code>(Builtins::builtin(Builtins::ConstructCall_DebugBreak));
1196 return result;
1197 }
1198 if (code->kind() == Code::STUB) {
1199 ASSERT(code->major_key() == CodeStub::CallFunction ||
1200 code->major_key() == CodeStub::StackCheck);
1201 Handle<Code> result =
1202 Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak));
1203 return result;
1204 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001205
1206 UNREACHABLE();
1207 return Handle<Code>::null();
1208}
1209
1210
1211// Simple function for returning the source positions for active break points.
1212Handle<Object> Debug::GetSourceBreakLocations(
1213 Handle<SharedFunctionInfo> shared) {
1214 if (!HasDebugInfo(shared)) return Handle<Object>(Heap::undefined_value());
1215 Handle<DebugInfo> debug_info = GetDebugInfo(shared);
1216 if (debug_info->GetBreakPointCount() == 0) {
1217 return Handle<Object>(Heap::undefined_value());
1218 }
1219 Handle<FixedArray> locations =
1220 Factory::NewFixedArray(debug_info->GetBreakPointCount());
1221 int count = 0;
1222 for (int i = 0; i < debug_info->break_points()->length(); i++) {
1223 if (!debug_info->break_points()->get(i)->IsUndefined()) {
1224 BreakPointInfo* break_point_info =
1225 BreakPointInfo::cast(debug_info->break_points()->get(i));
1226 if (break_point_info->GetBreakPointCount() > 0) {
1227 locations->set(count++, break_point_info->statement_position());
1228 }
1229 }
1230 }
1231 return locations;
1232}
1233
1234
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001235void Debug::NewBreak(StackFrame::Id break_frame_id) {
1236 thread_local_.break_frame_id_ = break_frame_id;
1237 thread_local_.break_id_ = ++thread_local_.break_count_;
1238}
1239
1240
1241void Debug::SetBreak(StackFrame::Id break_frame_id, int break_id) {
1242 thread_local_.break_frame_id_ = break_frame_id;
1243 thread_local_.break_id_ = break_id;
1244}
1245
1246
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001247// Handle stepping into a function.
1248void Debug::HandleStepIn(Handle<JSFunction> function,
1249 Address fp,
1250 bool is_constructor) {
1251 // If the frame pointer is not supplied by the caller find it.
1252 if (fp == 0) {
1253 StackFrameIterator it;
1254 it.Advance();
1255 // For constructor functions skip another frame.
1256 if (is_constructor) {
1257 ASSERT(it.frame()->is_construct());
1258 it.Advance();
1259 }
1260 fp = it.frame()->fp();
1261 }
1262
1263 // Flood the function with one-shot break points if it is called from where
1264 // step into was requested.
1265 if (fp == Debug::step_in_fp()) {
1266 // Don't allow step into functions in the native context.
1267 if (function->context()->global() != Top::context()->builtins()) {
kasperl@chromium.orgacae3782009-04-11 09:17:08 +00001268 if (function->shared()->code() ==
1269 Builtins::builtin(Builtins::FunctionApply) ||
1270 function->shared()->code() ==
1271 Builtins::builtin(Builtins::FunctionCall)) {
1272 // Handle function.apply and function.call separately to flood the
1273 // function to be called and not the code for Builtins::FunctionApply or
1274 // Builtins::FunctionCall. At the point of the call IC to call either
1275 // Builtins::FunctionApply or Builtins::FunctionCall the expression
1276 // stack has the following content:
1277 // symbol "apply" or "call"
1278 // function apply or call was called on
1279 // receiver for apply or call (first parameter to apply or call)
1280 // ... further arguments to apply or call.
1281 JavaScriptFrameIterator it;
1282 ASSERT(it.frame()->fp() == fp);
1283 ASSERT(it.frame()->GetExpression(1)->IsJSFunction());
1284 if (it.frame()->GetExpression(1)->IsJSFunction()) {
1285 Handle<JSFunction>
1286 actual_function(JSFunction::cast(it.frame()->GetExpression(1)));
1287 Handle<SharedFunctionInfo> actual_shared(actual_function->shared());
1288 Debug::FloodWithOneShot(actual_shared);
1289 }
1290 } else {
1291 Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
1292 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001293 }
1294 }
1295}
1296
1297
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001298void Debug::ClearStepping() {
1299 // Clear the various stepping setup.
1300 ClearOneShot();
1301 ClearStepIn();
1302 ClearStepNext();
1303
1304 // Clear multiple step counter.
1305 thread_local_.step_count_ = 0;
1306}
1307
1308// Clears all the one-shot break points that are currently set. Normally this
1309// function is called each time a break point is hit as one shot break points
1310// are used to support stepping.
1311void Debug::ClearOneShot() {
1312 // The current implementation just runs through all the breakpoints. When the
v8.team.kasperl727e9952008-09-02 14:56:44 +00001313 // last break point for a function is removed that function is automatically
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001314 // removed from the list.
1315
1316 DebugInfoListNode* node = debug_info_list_;
1317 while (node != NULL) {
1318 BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
1319 while (!it.Done()) {
1320 it.ClearOneShot();
1321 it.Next();
1322 }
1323 node = node->next();
1324 }
1325}
1326
1327
1328void Debug::ActivateStepIn(StackFrame* frame) {
1329 thread_local_.step_into_fp_ = frame->fp();
1330}
1331
1332
1333void Debug::ClearStepIn() {
1334 thread_local_.step_into_fp_ = 0;
1335}
1336
1337
1338void Debug::ClearStepNext() {
1339 thread_local_.last_step_action_ = StepNone;
ager@chromium.org236ad962008-09-25 09:45:57 +00001340 thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001341 thread_local_.last_fp_ = 0;
1342}
1343
1344
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001345bool Debug::EnsureCompiled(Handle<SharedFunctionInfo> shared) {
1346 if (shared->is_compiled()) return true;
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001347 return CompileLazyShared(shared, CLEAR_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001348}
1349
1350
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001351// Ensures the debug information is present for shared.
1352bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
1353 // Return if we already have the debug info for shared.
1354 if (HasDebugInfo(shared)) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001355
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001356 // Ensure shared in compiled. Return false if this failed.
1357 if (!EnsureCompiled(shared)) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001358
1359 // Create the debug info object.
v8.team.kasperl727e9952008-09-02 14:56:44 +00001360 Handle<DebugInfo> debug_info = Factory::NewDebugInfo(shared);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001361
1362 // Add debug info to the list.
1363 DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
1364 node->set_next(debug_info_list_);
1365 debug_info_list_ = node;
1366
1367 // Now there is at least one break point.
1368 has_break_points_ = true;
1369
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001370 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001371}
1372
1373
1374void Debug::RemoveDebugInfo(Handle<DebugInfo> debug_info) {
1375 ASSERT(debug_info_list_ != NULL);
1376 // Run through the debug info objects to find this one and remove it.
1377 DebugInfoListNode* prev = NULL;
1378 DebugInfoListNode* current = debug_info_list_;
1379 while (current != NULL) {
1380 if (*current->debug_info() == *debug_info) {
1381 // Unlink from list. If prev is NULL we are looking at the first element.
1382 if (prev == NULL) {
1383 debug_info_list_ = current->next();
1384 } else {
1385 prev->set_next(current->next());
1386 }
1387 current->debug_info()->shared()->set_debug_info(Heap::undefined_value());
1388 delete current;
1389
1390 // If there are no more debug info objects there are not more break
1391 // points.
1392 has_break_points_ = debug_info_list_ != NULL;
1393
1394 return;
1395 }
1396 // Move to next in list.
1397 prev = current;
1398 current = current->next();
1399 }
1400 UNREACHABLE();
1401}
1402
1403
1404void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00001405 HandleScope scope;
1406
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001407 // Get the executing function in which the debug break occurred.
1408 Handle<SharedFunctionInfo> shared =
1409 Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001410 if (!EnsureDebugInfo(shared)) {
1411 // Return if we failed to retrieve the debug info.
1412 return;
1413 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001414 Handle<DebugInfo> debug_info = GetDebugInfo(shared);
1415 Handle<Code> code(debug_info->code());
1416 Handle<Code> original_code(debug_info->original_code());
1417#ifdef DEBUG
1418 // Get the code which is actually executing.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00001419 Handle<Code> frame_code(frame->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001420 ASSERT(frame_code.is_identical_to(code));
1421#endif
1422
1423 // Find the call address in the running code. This address holds the call to
1424 // either a DebugBreakXXX or to the debug break return entry code if the
1425 // break point is still active after processing the break point.
1426 Address addr = frame->pc() - Assembler::kTargetAddrToReturnAddrDist;
1427
1428 // Check if the location is at JS exit.
1429 bool at_js_exit = false;
1430 RelocIterator it(debug_info->code());
1431 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001432 if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001433 at_js_exit = it.rinfo()->pc() == addr - 1;
1434 }
1435 it.next();
1436 }
1437
1438 // Handle the jump to continue execution after break point depending on the
1439 // break location.
1440 if (at_js_exit) {
1441 // First check if the call in the code is still the debug break return
1442 // entry code. If it is the break point is still active. If not the break
1443 // point was removed during break point processing.
1444 if (Assembler::target_address_at(addr) ==
1445 debug_break_return_entry()->entry()) {
1446 // Break point still active. Jump to the corresponding place in the
1447 // original code.
1448 addr += original_code->instruction_start() - code->instruction_start();
1449 }
1450
1451 // Move one byte back to where the call instruction was placed.
1452 thread_local_.after_break_target_ = addr - 1;
1453 } else {
1454 // Check if there still is a debug break call at the target address. If the
1455 // break point has been removed it will have disappeared. If it have
1456 // disappeared don't try to look in the original code as the running code
1457 // will have the right address. This takes care of the case where the last
1458 // break point is removed from the function and therefore no "original code"
1459 // is available. If the debug break call is still there find the address in
1460 // the original code.
1461 if (IsDebugBreak(Assembler::target_address_at(addr))) {
1462 // If the break point is still there find the call address which was
1463 // overwritten in the original code by the call to DebugBreakXXX.
1464
1465 // Find the corresponding address in the original code.
1466 addr += original_code->instruction_start() - code->instruction_start();
1467 }
1468
1469 // Install jump to the call address in the original code. This will be the
1470 // call which was overwritten by the call to DebugBreakXXX.
1471 thread_local_.after_break_target_ = Assembler::target_address_at(addr);
1472 }
1473}
1474
1475
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001476bool Debug::IsDebugGlobal(GlobalObject* global) {
1477 return IsLoaded() && global == Debug::debug_context()->global();
1478}
1479
1480
ager@chromium.org32912102009-01-16 10:38:43 +00001481void Debug::ClearMirrorCache() {
ager@chromium.org381abbb2009-02-25 13:23:22 +00001482 HandleScope scope;
ager@chromium.org32912102009-01-16 10:38:43 +00001483 ASSERT(Top::context() == *Debug::debug_context());
1484
1485 // Clear the mirror cache.
1486 Handle<String> function_name =
1487 Factory::LookupSymbol(CStrVector("ClearMirrorCache"));
1488 Handle<Object> fun(Top::global()->GetProperty(*function_name));
1489 ASSERT(fun->IsJSFunction());
1490 bool caught_exception;
1491 Handle<Object> js_object = Execution::TryCall(
1492 Handle<JSFunction>::cast(fun),
1493 Handle<JSObject>(Debug::debug_context()->global()),
1494 0, NULL, &caught_exception);
1495}
1496
1497
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001498// If an object given is an external string, check that the underlying
1499// resource is accessible. For other kinds of objects, always return true.
1500static bool IsExternalStringValid(Object* str) {
1501 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
1502 return true;
1503 }
1504 if (String::cast(str)->IsAsciiRepresentation()) {
1505 return ExternalAsciiString::cast(str)->resource() != NULL;
1506 } else if (String::cast(str)->IsTwoByteRepresentation()) {
1507 return ExternalTwoByteString::cast(str)->resource() != NULL;
1508 } else {
1509 return true;
1510 }
1511}
1512
1513
1514void Debug::CreateScriptCache() {
1515 HandleScope scope;
1516
1517 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
1518 // rid of all the cached script wrappers and the second gets rid of the
1519 // scripts which is no longer referenced.
1520 Heap::CollectAllGarbage();
1521 Heap::CollectAllGarbage();
1522
1523 ASSERT(script_cache_ == NULL);
1524 script_cache_ = new ScriptCache();
1525
1526 // Scan heap for Script objects.
1527 int count = 0;
1528 HeapIterator iterator;
1529 while (iterator.has_next()) {
1530 HeapObject* obj = iterator.next();
1531 ASSERT(obj != NULL);
1532 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
1533 script_cache_->Add(Handle<Script>(Script::cast(obj)));
1534 count++;
1535 }
1536 }
1537}
1538
1539
1540void Debug::DestroyScriptCache() {
1541 // Get rid of the script cache if it was created.
1542 if (script_cache_ != NULL) {
1543 delete script_cache_;
1544 script_cache_ = NULL;
1545 }
1546}
1547
1548
1549void Debug::AddScriptToScriptCache(Handle<Script> script) {
1550 if (script_cache_ != NULL) {
1551 script_cache_->Add(script);
1552 }
1553}
1554
1555
1556Handle<FixedArray> Debug::GetLoadedScripts() {
1557 // Create and fill the script cache when the loaded scripts is requested for
1558 // the first time.
1559 if (script_cache_ == NULL) {
1560 CreateScriptCache();
1561 }
1562
1563 // If the script cache is not active just return an empty array.
1564 ASSERT(script_cache_ != NULL);
1565 if (script_cache_ == NULL) {
1566 Factory::NewFixedArray(0);
1567 }
1568
1569 // Perform GC to get unreferenced scripts evicted from the cache before
1570 // returning the content.
1571 Heap::CollectAllGarbage();
1572
1573 // Get the scripts from the cache.
1574 return script_cache_->GetScripts();
1575}
1576
1577
1578void Debug::AfterGarbageCollection() {
1579 // Generate events for collected scripts.
1580 if (script_cache_ != NULL) {
1581 script_cache_->ProcessCollectedScripts();
1582 }
1583}
1584
1585
ager@chromium.org71daaf62009-04-01 07:22:49 +00001586Mutex* Debugger::debugger_access_ = OS::CreateMutex();
iposva@chromium.org245aa852009-02-10 00:49:54 +00001587Handle<Object> Debugger::event_listener_ = Handle<Object>();
1588Handle<Object> Debugger::event_listener_data_ = Handle<Object>();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589bool Debugger::compiling_natives_ = false;
mads.s.agercbaa0602008-08-14 13:41:48 +00001590bool Debugger::is_loading_debugger_ = false;
ager@chromium.org71daaf62009-04-01 07:22:49 +00001591bool Debugger::never_unload_debugger_ = false;
ager@chromium.org5ec48922009-05-05 07:25:34 +00001592v8::Debug::MessageHandler2 Debugger::message_handler_ = NULL;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001593bool Debugger::debugger_unload_pending_ = false;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001594v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
1595int Debugger::host_dispatch_micros_ = 100 * 1000;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001596DebuggerAgent* Debugger::agent_ = NULL;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00001597LockingCommandMessageQueue Debugger::command_queue_(kQueueInitialSize);
ager@chromium.org41826e72009-03-30 13:30:57 +00001598Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001599
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001600
1601Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
1602 int argc, Object*** argv,
1603 bool* caught_exception) {
1604 ASSERT(Top::context() == *Debug::debug_context());
1605
1606 // Create the execution state object.
1607 Handle<String> constructor_str = Factory::LookupSymbol(constructor_name);
1608 Handle<Object> constructor(Top::global()->GetProperty(*constructor_str));
1609 ASSERT(constructor->IsJSFunction());
1610 if (!constructor->IsJSFunction()) {
1611 *caught_exception = true;
1612 return Factory::undefined_value();
1613 }
1614 Handle<Object> js_object = Execution::TryCall(
1615 Handle<JSFunction>::cast(constructor),
1616 Handle<JSObject>(Debug::debug_context()->global()), argc, argv,
1617 caught_exception);
1618 return js_object;
1619}
1620
1621
1622Handle<Object> Debugger::MakeExecutionState(bool* caught_exception) {
1623 // Create the execution state object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001624 Handle<Object> break_id = Factory::NewNumberFromInt(Debug::break_id());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001625 const int argc = 1;
1626 Object** argv[argc] = { break_id.location() };
1627 return MakeJSObject(CStrVector("MakeExecutionState"),
1628 argc, argv, caught_exception);
1629}
1630
1631
1632Handle<Object> Debugger::MakeBreakEvent(Handle<Object> exec_state,
1633 Handle<Object> break_points_hit,
1634 bool* caught_exception) {
1635 // Create the new break event object.
1636 const int argc = 2;
1637 Object** argv[argc] = { exec_state.location(),
1638 break_points_hit.location() };
1639 return MakeJSObject(CStrVector("MakeBreakEvent"),
1640 argc,
1641 argv,
1642 caught_exception);
1643}
1644
1645
1646Handle<Object> Debugger::MakeExceptionEvent(Handle<Object> exec_state,
1647 Handle<Object> exception,
1648 bool uncaught,
1649 bool* caught_exception) {
1650 // Create the new exception event object.
1651 const int argc = 3;
1652 Object** argv[argc] = { exec_state.location(),
1653 exception.location(),
1654 uncaught ? Factory::true_value().location() :
1655 Factory::false_value().location()};
1656 return MakeJSObject(CStrVector("MakeExceptionEvent"),
1657 argc, argv, caught_exception);
1658}
1659
1660
1661Handle<Object> Debugger::MakeNewFunctionEvent(Handle<Object> function,
1662 bool* caught_exception) {
1663 // Create the new function event object.
1664 const int argc = 1;
1665 Object** argv[argc] = { function.location() };
1666 return MakeJSObject(CStrVector("MakeNewFunctionEvent"),
1667 argc, argv, caught_exception);
1668}
1669
1670
1671Handle<Object> Debugger::MakeCompileEvent(Handle<Script> script,
iposva@chromium.org245aa852009-02-10 00:49:54 +00001672 bool before,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001673 bool* caught_exception) {
1674 // Create the compile event object.
1675 Handle<Object> exec_state = MakeExecutionState(caught_exception);
iposva@chromium.org245aa852009-02-10 00:49:54 +00001676 Handle<Object> script_wrapper = GetScriptWrapper(script);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001677 const int argc = 3;
iposva@chromium.org245aa852009-02-10 00:49:54 +00001678 Object** argv[argc] = { exec_state.location(),
1679 script_wrapper.location(),
1680 before ? Factory::true_value().location() :
1681 Factory::false_value().location() };
1682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001683 return MakeJSObject(CStrVector("MakeCompileEvent"),
1684 argc,
1685 argv,
1686 caught_exception);
1687}
1688
1689
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001690Handle<Object> Debugger::MakeScriptCollectedEvent(int id,
1691 bool* caught_exception) {
1692 // Create the script collected event object.
1693 Handle<Object> exec_state = MakeExecutionState(caught_exception);
1694 Handle<Object> id_object = Handle<Smi>(Smi::FromInt(id));
1695 const int argc = 2;
1696 Object** argv[argc] = { exec_state.location(), id_object.location() };
1697
1698 return MakeJSObject(CStrVector("MakeScriptCollectedEvent"),
1699 argc,
1700 argv,
1701 caught_exception);
1702}
1703
1704
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001705void Debugger::OnException(Handle<Object> exception, bool uncaught) {
1706 HandleScope scope;
1707
1708 // Bail out based on state or if there is no listener for this event
1709 if (Debug::InDebugger()) return;
1710 if (!Debugger::EventActive(v8::Exception)) return;
1711
1712 // Bail out if exception breaks are not active
1713 if (uncaught) {
1714 // Uncaught exceptions are reported by either flags.
1715 if (!(Debug::break_on_uncaught_exception() ||
1716 Debug::break_on_exception())) return;
1717 } else {
1718 // Caught exceptions are reported is activated.
1719 if (!Debug::break_on_exception()) return;
1720 }
1721
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001722 // Enter the debugger.
1723 EnterDebugger debugger;
1724 if (debugger.FailedToEnter()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725
1726 // Clear all current stepping setup.
1727 Debug::ClearStepping();
1728 // Create the event data object.
1729 bool caught_exception = false;
1730 Handle<Object> exec_state = MakeExecutionState(&caught_exception);
1731 Handle<Object> event_data;
1732 if (!caught_exception) {
1733 event_data = MakeExceptionEvent(exec_state, exception, uncaught,
1734 &caught_exception);
1735 }
1736 // Bail out and don't call debugger if exception.
1737 if (caught_exception) {
1738 return;
1739 }
1740
ager@chromium.org5ec48922009-05-05 07:25:34 +00001741 // Process debug event.
1742 ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001743 // Return to continue execution from where the exception was thrown.
1744}
1745
1746
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001747void Debugger::OnDebugBreak(Handle<Object> break_points_hit,
1748 bool auto_continue) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001749 HandleScope scope;
1750
kasper.lund212ac232008-07-16 07:07:30 +00001751 // Debugger has already been entered by caller.
1752 ASSERT(Top::context() == *Debug::debug_context());
1753
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001754 // Bail out if there is no listener for this event
1755 if (!Debugger::EventActive(v8::Break)) return;
1756
1757 // Debugger must be entered in advance.
1758 ASSERT(Top::context() == *Debug::debug_context());
1759
1760 // Create the event data object.
1761 bool caught_exception = false;
1762 Handle<Object> exec_state = MakeExecutionState(&caught_exception);
1763 Handle<Object> event_data;
1764 if (!caught_exception) {
1765 event_data = MakeBreakEvent(exec_state, break_points_hit,
1766 &caught_exception);
1767 }
1768 // Bail out and don't call debugger if exception.
1769 if (caught_exception) {
1770 return;
1771 }
1772
ager@chromium.org5ec48922009-05-05 07:25:34 +00001773 // Process debug event.
1774 ProcessDebugEvent(v8::Break,
1775 Handle<JSObject>::cast(event_data),
1776 auto_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001777}
1778
1779
1780void Debugger::OnBeforeCompile(Handle<Script> script) {
1781 HandleScope scope;
1782
1783 // Bail out based on state or if there is no listener for this event
1784 if (Debug::InDebugger()) return;
1785 if (compiling_natives()) return;
1786 if (!EventActive(v8::BeforeCompile)) return;
1787
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001788 // Enter the debugger.
1789 EnterDebugger debugger;
1790 if (debugger.FailedToEnter()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001791
1792 // Create the event data object.
1793 bool caught_exception = false;
iposva@chromium.org245aa852009-02-10 00:49:54 +00001794 Handle<Object> event_data = MakeCompileEvent(script, true, &caught_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001795 // Bail out and don't call debugger if exception.
1796 if (caught_exception) {
1797 return;
1798 }
1799
ager@chromium.org5ec48922009-05-05 07:25:34 +00001800 // Process debug event.
1801 ProcessDebugEvent(v8::BeforeCompile,
1802 Handle<JSObject>::cast(event_data),
1803 true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001804}
1805
1806
1807// Handle debugger actions when a new script is compiled.
1808void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun) {
kasper.lund212ac232008-07-16 07:07:30 +00001809 HandleScope scope;
1810
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001811 // Add the newly compiled script to the script cache.
1812 Debug::AddScriptToScriptCache(script);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001813
1814 // No more to do if not debugging.
ager@chromium.org71daaf62009-04-01 07:22:49 +00001815 if (!IsDebuggerActive()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001816
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001817 // No compile events while compiling natives.
1818 if (compiling_natives()) return;
1819
iposva@chromium.org245aa852009-02-10 00:49:54 +00001820 // Store whether in debugger before entering debugger.
1821 bool in_debugger = Debug::InDebugger();
1822
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001823 // Enter the debugger.
1824 EnterDebugger debugger;
1825 if (debugger.FailedToEnter()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826
1827 // If debugging there might be script break points registered for this
1828 // script. Make sure that these break points are set.
1829
1830 // Get the function UpdateScriptBreakPoints (defined in debug-delay.js).
1831 Handle<Object> update_script_break_points =
1832 Handle<Object>(Debug::debug_context()->global()->GetProperty(
1833 *Factory::LookupAsciiSymbol("UpdateScriptBreakPoints")));
1834 if (!update_script_break_points->IsJSFunction()) {
1835 return;
1836 }
1837 ASSERT(update_script_break_points->IsJSFunction());
1838
1839 // Wrap the script object in a proper JS object before passing it
1840 // to JavaScript.
1841 Handle<JSValue> wrapper = GetScriptWrapper(script);
1842
1843 // Call UpdateScriptBreakPoints expect no exceptions.
kasper.lund212ac232008-07-16 07:07:30 +00001844 bool caught_exception = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001845 const int argc = 1;
1846 Object** argv[argc] = { reinterpret_cast<Object**>(wrapper.location()) };
1847 Handle<Object> result = Execution::TryCall(
1848 Handle<JSFunction>::cast(update_script_break_points),
1849 Top::builtins(), argc, argv,
1850 &caught_exception);
1851 if (caught_exception) {
1852 return;
1853 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 // Bail out based on state or if there is no listener for this event
iposva@chromium.org245aa852009-02-10 00:49:54 +00001855 if (in_debugger) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856 if (!Debugger::EventActive(v8::AfterCompile)) return;
1857
1858 // Create the compile state object.
1859 Handle<Object> event_data = MakeCompileEvent(script,
iposva@chromium.org245aa852009-02-10 00:49:54 +00001860 false,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001861 &caught_exception);
1862 // Bail out and don't call debugger if exception.
1863 if (caught_exception) {
1864 return;
1865 }
ager@chromium.org5ec48922009-05-05 07:25:34 +00001866 // Process debug event.
1867 ProcessDebugEvent(v8::AfterCompile,
1868 Handle<JSObject>::cast(event_data),
1869 true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001870}
1871
1872
1873void Debugger::OnNewFunction(Handle<JSFunction> function) {
1874 return;
1875 HandleScope scope;
1876
1877 // Bail out based on state or if there is no listener for this event
1878 if (Debug::InDebugger()) return;
1879 if (compiling_natives()) return;
1880 if (!Debugger::EventActive(v8::NewFunction)) return;
1881
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001882 // Enter the debugger.
1883 EnterDebugger debugger;
1884 if (debugger.FailedToEnter()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885
1886 // Create the event object.
1887 bool caught_exception = false;
1888 Handle<Object> event_data = MakeNewFunctionEvent(function, &caught_exception);
1889 // Bail out and don't call debugger if exception.
1890 if (caught_exception) {
1891 return;
1892 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001893 // Process debug event.
ager@chromium.org5ec48922009-05-05 07:25:34 +00001894 ProcessDebugEvent(v8::NewFunction, Handle<JSObject>::cast(event_data), true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001895}
1896
1897
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001898void Debugger::OnScriptCollected(int id) {
1899 HandleScope scope;
1900
1901 // No more to do if not debugging.
1902 if (!IsDebuggerActive()) return;
1903 if (!Debugger::EventActive(v8::ScriptCollected)) return;
1904
1905 // Enter the debugger.
1906 EnterDebugger debugger;
1907 if (debugger.FailedToEnter()) return;
1908
1909 // Create the script collected state object.
1910 bool caught_exception = false;
1911 Handle<Object> event_data = MakeScriptCollectedEvent(id,
1912 &caught_exception);
1913 // Bail out and don't call debugger if exception.
1914 if (caught_exception) {
1915 return;
1916 }
1917
1918 // Process debug event.
1919 ProcessDebugEvent(v8::ScriptCollected,
1920 Handle<JSObject>::cast(event_data),
1921 true);
1922}
1923
1924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001925void Debugger::ProcessDebugEvent(v8::DebugEvent event,
ager@chromium.org5ec48922009-05-05 07:25:34 +00001926 Handle<JSObject> event_data,
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001927 bool auto_continue) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00001928 HandleScope scope;
1929
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001930 // Clear any pending debug break if this is a real break.
1931 if (!auto_continue) {
1932 Debug::clear_interrupt_pending(DEBUGBREAK);
1933 }
1934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001935 // Create the execution state.
1936 bool caught_exception = false;
1937 Handle<Object> exec_state = MakeExecutionState(&caught_exception);
1938 if (caught_exception) {
1939 return;
1940 }
ager@chromium.org41826e72009-03-30 13:30:57 +00001941 // First notify the message handler if any.
1942 if (message_handler_ != NULL) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001943 NotifyMessageHandler(event,
1944 Handle<JSObject>::cast(exec_state),
1945 event_data,
1946 auto_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001947 }
iposva@chromium.org245aa852009-02-10 00:49:54 +00001948 // Notify registered debug event listener. This can be either a C or a
1949 // JavaScript function.
1950 if (!event_listener_.is_null()) {
1951 if (event_listener_->IsProxy()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001952 // C debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00001953 Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001954 v8::Debug::EventCallback callback =
1955 FUNCTION_CAST<v8::Debug::EventCallback>(callback_obj->proxy());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001956 callback(event,
1957 v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001958 v8::Utils::ToLocal(event_data),
iposva@chromium.org245aa852009-02-10 00:49:54 +00001959 v8::Utils::ToLocal(Handle<Object>::cast(event_listener_data_)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960 } else {
1961 // JavaScript debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00001962 ASSERT(event_listener_->IsJSFunction());
1963 Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001964
1965 // Invoke the JavaScript debug event listener.
1966 const int argc = 4;
1967 Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(),
1968 exec_state.location(),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001969 Handle<Object>::cast(event_data).location(),
iposva@chromium.org245aa852009-02-10 00:49:54 +00001970 event_listener_data_.location() };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001971 Handle<Object> result = Execution::TryCall(fun, Top::global(),
1972 argc, argv, &caught_exception);
1973 if (caught_exception) {
1974 // Silently ignore exceptions from debug event listeners.
1975 }
1976 }
1977 }
1978}
1979
1980
ager@chromium.org71daaf62009-04-01 07:22:49 +00001981void Debugger::UnloadDebugger() {
1982 // Make sure that there are no breakpoints left.
1983 Debug::ClearAllBreakPoints();
1984
1985 // Unload the debugger if feasible.
1986 if (!never_unload_debugger_) {
1987 Debug::Unload();
1988 }
1989
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001990 // Clear the flag indicating that the debugger should be unloaded.
1991 debugger_unload_pending_ = false;
ager@chromium.org71daaf62009-04-01 07:22:49 +00001992}
1993
1994
ager@chromium.org41826e72009-03-30 13:30:57 +00001995void Debugger::NotifyMessageHandler(v8::DebugEvent event,
ager@chromium.org5ec48922009-05-05 07:25:34 +00001996 Handle<JSObject> exec_state,
1997 Handle<JSObject> event_data,
ager@chromium.org41826e72009-03-30 13:30:57 +00001998 bool auto_continue) {
1999 HandleScope scope;
2000
2001 if (!Debug::Load()) return;
2002
2003 // Process the individual events.
ager@chromium.org5ec48922009-05-05 07:25:34 +00002004 bool sendEventMessage = false;
ager@chromium.org41826e72009-03-30 13:30:57 +00002005 switch (event) {
2006 case v8::Break:
ager@chromium.org5ec48922009-05-05 07:25:34 +00002007 sendEventMessage = !auto_continue;
ager@chromium.org41826e72009-03-30 13:30:57 +00002008 break;
2009 case v8::Exception:
ager@chromium.org5ec48922009-05-05 07:25:34 +00002010 sendEventMessage = true;
ager@chromium.org41826e72009-03-30 13:30:57 +00002011 break;
2012 case v8::BeforeCompile:
2013 break;
2014 case v8::AfterCompile:
ager@chromium.org5ec48922009-05-05 07:25:34 +00002015 sendEventMessage = true;
ager@chromium.org41826e72009-03-30 13:30:57 +00002016 break;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002017 case v8::ScriptCollected:
2018 sendEventMessage = true;
2019 break;
ager@chromium.org41826e72009-03-30 13:30:57 +00002020 case v8::NewFunction:
2021 break;
2022 default:
2023 UNREACHABLE();
2024 }
2025
ager@chromium.org5ec48922009-05-05 07:25:34 +00002026 // The debug command interrupt flag might have been set when the command was
2027 // added. It should be enough to clear the flag only once while we are in the
2028 // debugger.
2029 ASSERT(Debug::InDebugger());
2030 StackGuard::Continue(DEBUGCOMMAND);
2031
2032 // Notify the debugger that a debug event has occurred unless auto continue is
2033 // active in which case no event is send.
2034 if (sendEventMessage) {
2035 MessageImpl message = MessageImpl::NewEvent(
2036 event,
2037 auto_continue,
2038 Handle<JSObject>::cast(exec_state),
2039 Handle<JSObject>::cast(event_data));
2040 InvokeMessageHandler(message);
2041 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00002042
2043 // If auto continue don't make the event cause a break, but process messages
2044 // in the queue if any. For script collected events don't even process
2045 // messages in the queue as the execution state might not be what is expected
2046 // by the client.
ager@chromium.org6ffc2172009-05-29 19:20:16 +00002047 if ((auto_continue && !HasCommands()) || event == v8::ScriptCollected) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002048 return;
2049 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002050
2051 // Get the DebugCommandProcessor.
2052 v8::Local<v8::Object> api_exec_state =
2053 v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state));
2054 v8::Local<v8::String> fun_name =
2055 v8::String::New("debugCommandProcessor");
2056 v8::Local<v8::Function> fun =
2057 v8::Function::Cast(*api_exec_state->Get(fun_name));
2058 v8::TryCatch try_catch;
2059 v8::Local<v8::Object> cmd_processor =
2060 v8::Object::Cast(*fun->Call(api_exec_state, 0, NULL));
2061 if (try_catch.HasCaught()) {
2062 PrintLn(try_catch.Exception());
2063 return;
2064 }
2065
ager@chromium.org41826e72009-03-30 13:30:57 +00002066 // Process requests from the debugger.
2067 while (true) {
2068 // Wait for new command in the queue.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002069 if (Debugger::host_dispatch_handler_) {
2070 // In case there is a host dispatch - do periodic dispatches.
2071 if (!command_received_->Wait(host_dispatch_micros_)) {
2072 // Timout expired, do the dispatch.
2073 Debugger::host_dispatch_handler_();
2074 continue;
2075 }
2076 } else {
2077 // In case there is no host dispatch - just wait.
2078 command_received_->Wait();
2079 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002080
ager@chromium.org41826e72009-03-30 13:30:57 +00002081 // Get the command from the queue.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002082 CommandMessage command = command_queue_.Get();
ager@chromium.org41826e72009-03-30 13:30:57 +00002083 Logger::DebugTag("Got request from command queue, in interactive loop.");
ager@chromium.org71daaf62009-04-01 07:22:49 +00002084 if (!Debugger::IsDebuggerActive()) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002085 // Delete command text and user data.
2086 command.Dispose();
ager@chromium.org41826e72009-03-30 13:30:57 +00002087 return;
2088 }
2089
ager@chromium.org41826e72009-03-30 13:30:57 +00002090 // Invoke JavaScript to process the debug request.
2091 v8::Local<v8::String> fun_name;
2092 v8::Local<v8::Function> fun;
2093 v8::Local<v8::Value> request;
2094 v8::TryCatch try_catch;
2095 fun_name = v8::String::New("processDebugRequest");
2096 fun = v8::Function::Cast(*cmd_processor->Get(fun_name));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002097
2098 request = v8::String::New(command.text().start(),
2099 command.text().length());
ager@chromium.org41826e72009-03-30 13:30:57 +00002100 static const int kArgc = 1;
2101 v8::Handle<Value> argv[kArgc] = { request };
2102 v8::Local<v8::Value> response_val = fun->Call(cmd_processor, kArgc, argv);
2103
2104 // Get the response.
2105 v8::Local<v8::String> response;
2106 bool running = false;
2107 if (!try_catch.HasCaught()) {
2108 // Get response string.
2109 if (!response_val->IsUndefined()) {
2110 response = v8::String::Cast(*response_val);
2111 } else {
2112 response = v8::String::New("");
2113 }
2114
2115 // Log the JSON request/response.
2116 if (FLAG_trace_debug_json) {
2117 PrintLn(request);
2118 PrintLn(response);
2119 }
2120
2121 // Get the running state.
2122 fun_name = v8::String::New("isRunning");
2123 fun = v8::Function::Cast(*cmd_processor->Get(fun_name));
2124 static const int kArgc = 1;
2125 v8::Handle<Value> argv[kArgc] = { response };
2126 v8::Local<v8::Value> running_val = fun->Call(cmd_processor, kArgc, argv);
2127 if (!try_catch.HasCaught()) {
2128 running = running_val->ToBoolean()->Value();
2129 }
2130 } else {
2131 // In case of failure the result text is the exception text.
2132 response = try_catch.Exception()->ToString();
2133 }
2134
ager@chromium.org41826e72009-03-30 13:30:57 +00002135 // Return the result.
ager@chromium.org5ec48922009-05-05 07:25:34 +00002136 MessageImpl message = MessageImpl::NewResponse(
2137 event,
2138 running,
2139 Handle<JSObject>::cast(exec_state),
2140 Handle<JSObject>::cast(event_data),
2141 Handle<String>(Utils::OpenHandle(*response)),
2142 command.client_data());
2143 InvokeMessageHandler(message);
2144 command.Dispose();
ager@chromium.org41826e72009-03-30 13:30:57 +00002145
2146 // Return from debug event processing if either the VM is put into the
2147 // runnning state (through a continue command) or auto continue is active
2148 // and there are no more commands queued.
2149 if (running || (auto_continue && !HasCommands())) {
2150 return;
2151 }
2152 }
2153}
2154
2155
iposva@chromium.org245aa852009-02-10 00:49:54 +00002156void Debugger::SetEventListener(Handle<Object> callback,
2157 Handle<Object> data) {
2158 HandleScope scope;
2159
2160 // Clear the global handles for the event listener and the event listener data
2161 // object.
2162 if (!event_listener_.is_null()) {
2163 GlobalHandles::Destroy(
2164 reinterpret_cast<Object**>(event_listener_.location()));
2165 event_listener_ = Handle<Object>();
2166 }
2167 if (!event_listener_data_.is_null()) {
2168 GlobalHandles::Destroy(
2169 reinterpret_cast<Object**>(event_listener_data_.location()));
2170 event_listener_data_ = Handle<Object>();
2171 }
2172
2173 // If there is a new debug event listener register it together with its data
2174 // object.
2175 if (!callback->IsUndefined() && !callback->IsNull()) {
2176 event_listener_ = Handle<Object>::cast(GlobalHandles::Create(*callback));
2177 if (data.is_null()) {
2178 data = Factory::undefined_value();
2179 }
2180 event_listener_data_ = Handle<Object>::cast(GlobalHandles::Create(*data));
2181 }
2182
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002183 ListenersChanged();
iposva@chromium.org245aa852009-02-10 00:49:54 +00002184}
2185
2186
ager@chromium.org5ec48922009-05-05 07:25:34 +00002187void Debugger::SetMessageHandler(v8::Debug::MessageHandler2 handler) {
ager@chromium.org71daaf62009-04-01 07:22:49 +00002188 ScopedLock with(debugger_access_);
2189
ager@chromium.org381abbb2009-02-25 13:23:22 +00002190 message_handler_ = handler;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002191 ListenersChanged();
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002192 if (handler == NULL) {
ager@chromium.org71daaf62009-04-01 07:22:49 +00002193 // Send an empty command to the debugger if in a break to make JavaScript
2194 // run again if the debugger is closed.
2195 if (Debug::InDebugger()) {
2196 ProcessCommand(Vector<const uint16_t>::empty());
2197 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002198 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002199}
2200
2201
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002202void Debugger::ListenersChanged() {
2203 if (IsDebuggerActive()) {
2204 // Disable the compilation cache when the debugger is active.
2205 CompilationCache::Disable();
2206 } else {
2207 CompilationCache::Enable();
2208
2209 // Unload the debugger if event listener and message handler cleared.
2210 if (Debug::InDebugger()) {
2211 // If we are in debugger set the flag to unload the debugger when last
2212 // EnterDebugger on the current stack is destroyed.
2213 debugger_unload_pending_ = true;
2214 } else {
2215 UnloadDebugger();
2216 }
2217 }
2218}
2219
2220
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002221void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
2222 int period) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002223 host_dispatch_handler_ = handler;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002224 host_dispatch_micros_ = period * 1000;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002225}
2226
2227
ager@chromium.org41826e72009-03-30 13:30:57 +00002228// Calls the registered debug message handler. This callback is part of the
ager@chromium.org5ec48922009-05-05 07:25:34 +00002229// public API.
2230void Debugger::InvokeMessageHandler(MessageImpl message) {
ager@chromium.org71daaf62009-04-01 07:22:49 +00002231 ScopedLock with(debugger_access_);
2232
ager@chromium.org381abbb2009-02-25 13:23:22 +00002233 if (message_handler_ != NULL) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002234 message_handler_(message);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002235 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002236}
2237
2238
2239// Puts a command coming from the public API on the queue. Creates
2240// a copy of the command string managed by the debugger. Up to this
2241// point, the command data was managed by the API client. Called
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002242// by the API client thread.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002243void Debugger::ProcessCommand(Vector<const uint16_t> command,
2244 v8::Debug::ClientData* client_data) {
2245 // Need to cast away const.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002246 CommandMessage message = CommandMessage::New(
ager@chromium.org41826e72009-03-30 13:30:57 +00002247 Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002248 command.length()),
2249 client_data);
ager@chromium.org41826e72009-03-30 13:30:57 +00002250 Logger::DebugTag("Put command on command_queue.");
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002251 command_queue_.Put(message);
ager@chromium.org41826e72009-03-30 13:30:57 +00002252 command_received_->Signal();
sgjesse@chromium.org3afc1582009-04-16 22:31:44 +00002253
2254 // Set the debug command break flag to have the command processed.
ager@chromium.org41826e72009-03-30 13:30:57 +00002255 if (!Debug::InDebugger()) {
2256 StackGuard::DebugCommand();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002257 }
2258}
2259
2260
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002261bool Debugger::HasCommands() {
ager@chromium.org41826e72009-03-30 13:30:57 +00002262 return !command_queue_.IsEmpty();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002263}
2264
2265
ager@chromium.org71daaf62009-04-01 07:22:49 +00002266bool Debugger::IsDebuggerActive() {
2267 ScopedLock with(debugger_access_);
2268
2269 return message_handler_ != NULL || !event_listener_.is_null();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002270}
2271
2272
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002273Handle<Object> Debugger::Call(Handle<JSFunction> fun,
2274 Handle<Object> data,
2275 bool* pending_exception) {
ager@chromium.org71daaf62009-04-01 07:22:49 +00002276 // When calling functions in the debugger prevent it from beeing unloaded.
2277 Debugger::never_unload_debugger_ = true;
2278
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002279 // Enter the debugger.
2280 EnterDebugger debugger;
2281 if (debugger.FailedToEnter() || !debugger.HasJavaScriptFrames()) {
2282 return Factory::undefined_value();
2283 }
2284
2285 // Create the execution state.
2286 bool caught_exception = false;
2287 Handle<Object> exec_state = MakeExecutionState(&caught_exception);
2288 if (caught_exception) {
2289 return Factory::undefined_value();
2290 }
2291
2292 static const int kArgc = 2;
2293 Object** argv[kArgc] = { exec_state.location(), data.location() };
2294 Handle<Object> result = Execution::Call(fun, Factory::undefined_value(),
2295 kArgc, argv, pending_exception);
2296 return result;
2297}
2298
2299
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002300bool Debugger::StartAgent(const char* name, int port) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002301 if (Socket::Setup()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002302 agent_ = new DebuggerAgent(name, port);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002303 agent_->Start();
2304 return true;
2305 }
2306
2307 return false;
2308}
2309
2310
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002311void Debugger::StopAgent() {
2312 if (agent_ != NULL) {
2313 agent_->Shutdown();
2314 agent_->Join();
2315 delete agent_;
2316 agent_ = NULL;
2317 }
2318}
2319
2320
ager@chromium.org5ec48922009-05-05 07:25:34 +00002321MessageImpl MessageImpl::NewEvent(DebugEvent event,
2322 bool running,
2323 Handle<JSObject> exec_state,
2324 Handle<JSObject> event_data) {
2325 MessageImpl message(true, event, running,
2326 exec_state, event_data, Handle<String>(), NULL);
2327 return message;
2328}
2329
2330
2331MessageImpl MessageImpl::NewResponse(DebugEvent event,
2332 bool running,
2333 Handle<JSObject> exec_state,
2334 Handle<JSObject> event_data,
2335 Handle<String> response_json,
2336 v8::Debug::ClientData* client_data) {
2337 MessageImpl message(false, event, running,
2338 exec_state, event_data, response_json, client_data);
2339 return message;
2340}
2341
2342
2343MessageImpl::MessageImpl(bool is_event,
2344 DebugEvent event,
2345 bool running,
2346 Handle<JSObject> exec_state,
2347 Handle<JSObject> event_data,
2348 Handle<String> response_json,
2349 v8::Debug::ClientData* client_data)
2350 : is_event_(is_event),
2351 event_(event),
2352 running_(running),
2353 exec_state_(exec_state),
2354 event_data_(event_data),
2355 response_json_(response_json),
2356 client_data_(client_data) {}
2357
2358
2359bool MessageImpl::IsEvent() const {
2360 return is_event_;
2361}
2362
2363
2364bool MessageImpl::IsResponse() const {
2365 return !is_event_;
2366}
2367
2368
2369DebugEvent MessageImpl::GetEvent() const {
2370 return event_;
2371}
2372
2373
2374bool MessageImpl::WillStartRunning() const {
2375 return running_;
2376}
2377
2378
2379v8::Handle<v8::Object> MessageImpl::GetExecutionState() const {
2380 return v8::Utils::ToLocal(exec_state_);
2381}
2382
2383
2384v8::Handle<v8::Object> MessageImpl::GetEventData() const {
2385 return v8::Utils::ToLocal(event_data_);
2386}
2387
2388
2389v8::Handle<v8::String> MessageImpl::GetJSON() const {
2390 v8::HandleScope scope;
2391
2392 if (IsEvent()) {
2393 // Call toJSONProtocol on the debug event object.
2394 Handle<Object> fun = GetProperty(event_data_, "toJSONProtocol");
2395 if (!fun->IsJSFunction()) {
2396 return v8::Handle<v8::String>();
2397 }
2398 bool caught_exception;
2399 Handle<Object> json = Execution::TryCall(Handle<JSFunction>::cast(fun),
2400 event_data_,
2401 0, NULL, &caught_exception);
2402 if (caught_exception || !json->IsString()) {
2403 return v8::Handle<v8::String>();
2404 }
2405 return scope.Close(v8::Utils::ToLocal(Handle<String>::cast(json)));
2406 } else {
2407 return v8::Utils::ToLocal(response_json_);
2408 }
2409}
2410
2411
2412v8::Handle<v8::Context> MessageImpl::GetEventContext() const {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002413 Handle<Context> context = Debug::debugger_entry()->GetContext();
2414 // Top::context() may have been NULL when "script collected" event occured.
2415 if (*context == NULL) {
2416 ASSERT(event_ == v8::ScriptCollected);
2417 return v8::Local<v8::Context>();
2418 }
2419 Handle<Context> global_context(context->global_context());
2420 return v8::Utils::ToLocal(global_context);
ager@chromium.org5ec48922009-05-05 07:25:34 +00002421}
2422
2423
2424v8::Debug::ClientData* MessageImpl::GetClientData() const {
2425 return client_data_;
2426}
2427
2428
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002429CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
2430 client_data_(NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431}
2432
2433
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002434CommandMessage::CommandMessage(const Vector<uint16_t>& text,
2435 v8::Debug::ClientData* data)
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002436 : text_(text),
2437 client_data_(data) {
2438}
2439
2440
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002441CommandMessage::~CommandMessage() {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002442}
2443
2444
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002445void CommandMessage::Dispose() {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002446 text_.Dispose();
2447 delete client_data_;
2448 client_data_ = NULL;
2449}
2450
2451
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002452CommandMessage CommandMessage::New(const Vector<uint16_t>& command,
2453 v8::Debug::ClientData* data) {
2454 return CommandMessage(command.Clone(), data);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002455}
2456
2457
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002458CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
2459 size_(size) {
2460 messages_ = NewArray<CommandMessage>(size);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002461}
2462
2463
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002464CommandMessageQueue::~CommandMessageQueue() {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002465 while (!IsEmpty()) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002466 CommandMessage m = Get();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002467 m.Dispose();
2468 }
kasper.lund7276f142008-07-30 08:49:36 +00002469 DeleteArray(messages_);
2470}
2471
2472
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002473CommandMessage CommandMessageQueue::Get() {
kasper.lund7276f142008-07-30 08:49:36 +00002474 ASSERT(!IsEmpty());
2475 int result = start_;
2476 start_ = (start_ + 1) % size_;
2477 return messages_[result];
2478}
2479
2480
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002481void CommandMessageQueue::Put(const CommandMessage& message) {
kasper.lund7276f142008-07-30 08:49:36 +00002482 if ((end_ + 1) % size_ == start_) {
2483 Expand();
2484 }
2485 messages_[end_] = message;
2486 end_ = (end_ + 1) % size_;
2487}
2488
2489
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002490void CommandMessageQueue::Expand() {
2491 CommandMessageQueue new_queue(size_ * 2);
kasper.lund7276f142008-07-30 08:49:36 +00002492 while (!IsEmpty()) {
2493 new_queue.Put(Get());
2494 }
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002495 CommandMessage* array_to_free = messages_;
kasper.lund7276f142008-07-30 08:49:36 +00002496 *this = new_queue;
2497 new_queue.messages_ = array_to_free;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002498 // Make the new_queue empty so that it doesn't call Dispose on any messages.
2499 new_queue.start_ = new_queue.end_;
kasper.lund7276f142008-07-30 08:49:36 +00002500 // Automatic destructor called on new_queue, freeing array_to_free.
2501}
2502
2503
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002504LockingCommandMessageQueue::LockingCommandMessageQueue(int size)
2505 : queue_(size) {
kasper.lund7276f142008-07-30 08:49:36 +00002506 lock_ = OS::CreateMutex();
2507}
2508
2509
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002510LockingCommandMessageQueue::~LockingCommandMessageQueue() {
kasper.lund7276f142008-07-30 08:49:36 +00002511 delete lock_;
2512}
2513
2514
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002515bool LockingCommandMessageQueue::IsEmpty() const {
kasper.lund7276f142008-07-30 08:49:36 +00002516 ScopedLock sl(lock_);
2517 return queue_.IsEmpty();
2518}
2519
2520
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002521CommandMessage LockingCommandMessageQueue::Get() {
kasper.lund7276f142008-07-30 08:49:36 +00002522 ScopedLock sl(lock_);
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002523 CommandMessage result = queue_.Get();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002524 Logger::DebugEvent("Get", result.text());
kasper.lund7276f142008-07-30 08:49:36 +00002525 return result;
2526}
2527
2528
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002529void LockingCommandMessageQueue::Put(const CommandMessage& message) {
kasper.lund7276f142008-07-30 08:49:36 +00002530 ScopedLock sl(lock_);
2531 queue_.Put(message);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002532 Logger::DebugEvent("Put", message.text());
kasper.lund7276f142008-07-30 08:49:36 +00002533}
2534
2535
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00002536void LockingCommandMessageQueue::Clear() {
kasper.lund7276f142008-07-30 08:49:36 +00002537 ScopedLock sl(lock_);
2538 queue_.Clear();
2539}
2540
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002541#endif // ENABLE_DEBUGGER_SUPPORT
kasper.lund7276f142008-07-30 08:49:36 +00002542
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002543} } // namespace v8::internal