Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(329)

Side by Side Diff: src/debug/liveedit.cc

Issue 2636913002: [liveedit] reimplement frame restarting. (Closed)
Patch Set: rebase Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/debug/liveedit.h ('k') | src/debug/mips/debug-mips.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/debug/liveedit.h" 5 #include "src/debug/liveedit.h"
6 6
7 #include "src/ast/scopes.h" 7 #include "src/ast/scopes.h"
8 #include "src/code-stubs.h" 8 #include "src/code-stubs.h"
9 #include "src/compilation-cache.h" 9 #include "src/compilation-cache.h"
10 #include "src/compiler.h" 10 #include "src/compiler.h"
(...skipping 634 matching lines...) Expand 10 before | Expand all | Expand 10 after
645 645
646 646
647 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() { 647 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
648 Handle<Object> element = this->GetField(kSharedInfoOffset_); 648 Handle<Object> element = this->GetField(kSharedInfoOffset_);
649 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element); 649 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
650 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper); 650 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
651 } 651 }
652 652
653 653
654 void LiveEdit::InitializeThreadLocal(Debug* debug) { 654 void LiveEdit::InitializeThreadLocal(Debug* debug) {
655 debug->thread_local_.frame_drop_mode_ = LIVE_EDIT_FRAMES_UNTOUCHED; 655 debug->thread_local_.restart_fp_ = 0;
656 } 656 }
657 657
658 658
659 bool LiveEdit::SetAfterBreakTarget(Debug* debug) {
660 Code* code = NULL;
661 Isolate* isolate = debug->isolate_;
662 switch (debug->thread_local_.frame_drop_mode_) {
663 case LIVE_EDIT_FRAMES_UNTOUCHED:
664 return false;
665 case LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL:
666 // Debug break slot stub does not return normally, instead it manually
667 // cleans the stack and jumps. We should patch the jump address.
668 code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
669 break;
670 case LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL:
671 // Nothing to do, after_break_target is not used here.
672 return true;
673 case LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL:
674 code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
675 break;
676 case LIVE_EDIT_CURRENTLY_SET_MODE:
677 UNREACHABLE();
678 break;
679 }
680 debug->after_break_target_ = code->entry();
681 return true;
682 }
683
684
685 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script, 659 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
686 Handle<String> source) { 660 Handle<String> source) {
687 Isolate* isolate = script->GetIsolate(); 661 Isolate* isolate = script->GetIsolate();
688 662
689 MaybeHandle<JSArray> infos; 663 MaybeHandle<JSArray> infos;
690 Handle<Object> original_source = 664 Handle<Object> original_source =
691 Handle<Object>(script->source(), isolate); 665 Handle<Object>(script->source(), isolate);
692 script->set_source(*source); 666 script->set_source(*source);
693 667
694 { 668 {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
738 // A logical 'finally' section. 712 // A logical 'finally' section.
739 script->set_source(*original_source); 713 script->set_source(*original_source);
740 714
741 if (rethrow_exception.is_null()) { 715 if (rethrow_exception.is_null()) {
742 return infos.ToHandleChecked(); 716 return infos.ToHandleChecked();
743 } else { 717 } else {
744 return isolate->Throw<JSArray>(rethrow_exception); 718 return isolate->Throw<JSArray>(rethrow_exception);
745 } 719 }
746 } 720 }
747 721
748
749 // Visitor that finds all references to a particular code object,
750 // including "CODE_TARGET" references in other code objects and replaces
751 // them on the fly.
752 class ReplacingVisitor : public ObjectVisitor {
753 public:
754 explicit ReplacingVisitor(Code* original, Code* substitution)
755 : original_(original), substitution_(substitution) {
756 }
757
758 void VisitPointers(Object** start, Object** end) override {
759 for (Object** p = start; p < end; p++) {
760 if (*p == original_) {
761 *p = substitution_;
762 }
763 }
764 }
765
766 void VisitCodeEntry(Address entry) override {
767 if (Code::GetObjectFromEntryAddress(entry) == original_) {
768 Address substitution_entry = substitution_->instruction_start();
769 Memory::Address_at(entry) = substitution_entry;
770 }
771 }
772
773 void VisitCodeTarget(RelocInfo* rinfo) override {
774 if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
775 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
776 Address substitution_entry = substitution_->instruction_start();
777 rinfo->set_target_address(substitution_entry);
778 }
779 }
780
781 void VisitDebugTarget(RelocInfo* rinfo) override { VisitCodeTarget(rinfo); }
782
783 private:
784 Code* original_;
785 Code* substitution_;
786 };
787
788
789 // Finds all references to original and replaces them with substitution. 722 // Finds all references to original and replaces them with substitution.
790 static void ReplaceCodeObject(Handle<Code> original, 723 static void ReplaceCodeObject(Handle<Code> original,
791 Handle<Code> substitution) { 724 Handle<Code> substitution) {
792 // Perform a full GC in order to ensure that we are not in the middle of an 725 // Perform a full GC in order to ensure that we are not in the middle of an
793 // incremental marking phase when we are replacing the code object. 726 // incremental marking phase when we are replacing the code object.
794 // Since we are not in an incremental marking phase we can write pointers 727 // Since we are not in an incremental marking phase we can write pointers
795 // to code objects (that are never in new space) without worrying about 728 // to code objects (that are never in new space) without worrying about
796 // write barriers. 729 // write barriers.
797 Heap* heap = original->GetHeap(); 730 Heap* heap = original->GetHeap();
798 HeapIterator iterator(heap); 731 HeapIterator iterator(heap);
799
800 DCHECK(!heap->InNewSpace(*substitution));
801
802 ReplacingVisitor visitor(*original, *substitution);
803
804 // Iterate over all roots. Stack frames may have pointer into original code,
805 // so temporary replace the pointers with offset numbers
806 // in prologue/epilogue.
807 heap->IterateRoots(&visitor, VISIT_ALL);
808
809 // Now iterate over all pointers of all objects, including code_target 732 // Now iterate over all pointers of all objects, including code_target
810 // implicit pointers. 733 // implicit pointers.
811 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 734 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
812 obj->Iterate(&visitor); 735 if (obj->IsJSFunction()) {
736 JSFunction* fun = JSFunction::cast(obj);
737 if (fun->code() == *original) fun->ReplaceCode(*substitution);
738 } else if (obj->IsSharedFunctionInfo()) {
739 SharedFunctionInfo* info = SharedFunctionInfo::cast(obj);
740 if (info->code() == *original) info->set_code(*substitution);
741 }
813 } 742 }
814 } 743 }
815 744
816 745
817 // Patch function literals. 746 // Patch function literals.
818 // Name 'literals' is a misnomer. Rather it's a cache for complex object 747 // Name 'literals' is a misnomer. Rather it's a cache for complex object
819 // boilerplates and for a native context. We must clean cached values. 748 // boilerplates and for a native context. We must clean cached values.
820 // Additionally we may need to allocate a new array if number of literals 749 // Additionally we may need to allocate a new array if number of literals
821 // changed. 750 // changed.
822 class LiteralFixer { 751 class LiteralFixer {
(...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after
1270 UnwrapSharedFunctionInfoFromJSValue(jsvalue); 1199 UnwrapSharedFunctionInfoFromJSValue(jsvalue);
1271 1200
1272 if (function->Inlines(*shared)) { 1201 if (function->Inlines(*shared)) {
1273 SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate)); 1202 SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
1274 return true; 1203 return true;
1275 } 1204 }
1276 } 1205 }
1277 return false; 1206 return false;
1278 } 1207 }
1279 1208
1280
1281 // Iterates over handler chain and removes all elements that are inside
1282 // frames being dropped.
1283 static bool FixTryCatchHandler(StackFrame* top_frame,
1284 StackFrame* bottom_frame) {
1285 Address* pointer_address =
1286 &Memory::Address_at(top_frame->isolate()->get_address_from_id(
1287 Isolate::kHandlerAddress));
1288
1289 while (*pointer_address < top_frame->sp()) {
1290 pointer_address = &Memory::Address_at(*pointer_address);
1291 }
1292 Address* above_frame_address = pointer_address;
1293 while (*pointer_address < bottom_frame->fp()) {
1294 pointer_address = &Memory::Address_at(*pointer_address);
1295 }
1296 bool change = *above_frame_address != *pointer_address;
1297 *above_frame_address = *pointer_address;
1298 return change;
1299 }
1300
1301
1302 // Initializes an artificial stack frame. The data it contains is used for:
1303 // a. successful work of frame dropper code which eventually gets control,
1304 // b. being compatible with a typed frame structure for various stack
1305 // iterators.
1306 // Frame structure (conforms to InternalFrame structure):
1307 // -- function
1308 // -- code
1309 // -- SMI marker
1310 // -- frame base
1311 static void SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
1312 Handle<Code> code) {
1313 DCHECK(bottom_js_frame->is_java_script());
1314 Address fp = bottom_js_frame->fp();
1315 Memory::Object_at(fp + FrameDropperFrameConstants::kFunctionOffset) =
1316 Memory::Object_at(fp + StandardFrameConstants::kFunctionOffset);
1317 Memory::Object_at(fp + FrameDropperFrameConstants::kFrameTypeOffset) =
1318 Smi::FromInt(StackFrame::INTERNAL);
1319 Memory::Object_at(fp + FrameDropperFrameConstants::kCodeOffset) = *code;
1320 }
1321
1322
1323 // Removes specified range of frames from stack. There may be 1 or more
1324 // frames in range. Anyway the bottom frame is restarted rather than dropped,
1325 // and therefore has to be a JavaScript frame.
1326 // Returns error message or NULL.
1327 static const char* DropFrames(Vector<StackFrame*> frames, int top_frame_index,
1328 int bottom_js_frame_index,
1329 LiveEditFrameDropMode* mode) {
1330 if (!LiveEdit::kFrameDropperSupported) {
1331 return "Stack manipulations are not supported in this architecture.";
1332 }
1333
1334 StackFrame* pre_top_frame = frames[top_frame_index - 1];
1335 StackFrame* top_frame = frames[top_frame_index];
1336 StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
1337
1338 DCHECK(bottom_js_frame->is_java_script());
1339
1340 // Check the nature of the top frame.
1341 Isolate* isolate = bottom_js_frame->isolate();
1342 Code* pre_top_frame_code = pre_top_frame->LookupCode();
1343 bool frame_has_padding = true;
1344 if (pre_top_frame_code ==
1345 isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) {
1346 // OK, we can drop debug break slot.
1347 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
1348 } else if (pre_top_frame_code ==
1349 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) {
1350 // OK, we can drop our own code.
1351 pre_top_frame = frames[top_frame_index - 2];
1352 top_frame = frames[top_frame_index - 1];
1353 *mode = LIVE_EDIT_CURRENTLY_SET_MODE;
1354 frame_has_padding = false;
1355 } else if (pre_top_frame_code ==
1356 isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
1357 *mode = LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL;
1358 } else if (pre_top_frame_code->kind() == Code::STUB &&
1359 CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) {
1360 // Entry from our unit tests on 'debugger' statement.
1361 // It's fine, we support this case.
1362 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL;
1363 // We don't have a padding from 'debugger' statement call.
1364 // Here the stub is CEntry, it's not debug-only and can't be padded.
1365 // If anyone would complain, a proxy padded stub could be added.
1366 frame_has_padding = false;
1367 } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
1368 // This must be adaptor that remain from the frame dropping that
1369 // is still on stack. A frame dropper frame must be above it.
1370 DCHECK(frames[top_frame_index - 2]->LookupCode() ==
1371 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
1372 pre_top_frame = frames[top_frame_index - 3];
1373 top_frame = frames[top_frame_index - 2];
1374 *mode = LIVE_EDIT_CURRENTLY_SET_MODE;
1375 frame_has_padding = false;
1376 } else if (pre_top_frame_code->kind() == Code::BYTECODE_HANDLER) {
1377 // Interpreted bytecode takes up two stack frames, one for the bytecode
1378 // handler and one for the interpreter entry trampoline. Therefore we shift
1379 // up by one frame.
1380 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL;
1381 pre_top_frame = frames[top_frame_index - 2];
1382 top_frame = frames[top_frame_index - 1];
1383 } else {
1384 return "Unknown structure of stack above changing function";
1385 }
1386
1387 Address unused_stack_top = top_frame->sp();
1388 Address unused_stack_bottom =
1389 bottom_js_frame->fp() - FrameDropperFrameConstants::kFixedFrameSize +
1390 2 * kPointerSize; // Bigger address end is exclusive.
1391
1392 Address* top_frame_pc_address = top_frame->pc_address();
1393
1394 // top_frame may be damaged below this point. Do not used it.
1395 DCHECK(!(top_frame = NULL));
1396
1397 if (unused_stack_top > unused_stack_bottom) {
1398 if (frame_has_padding) {
1399 int shortage_bytes =
1400 static_cast<int>(unused_stack_top - unused_stack_bottom);
1401
1402 Address padding_start =
1403 pre_top_frame->fp() -
1404 (FrameDropperFrameConstants::kFixedFrameSize - kPointerSize);
1405
1406 Address padding_pointer = padding_start;
1407 Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue);
1408 while (Memory::Object_at(padding_pointer) == padding_object) {
1409 padding_pointer -= kPointerSize;
1410 }
1411 int padding_counter =
1412 Smi::cast(Memory::Object_at(padding_pointer))->value();
1413 if (padding_counter * kPointerSize < shortage_bytes) {
1414 return "Not enough space for frame dropper frame "
1415 "(even with padding frame)";
1416 }
1417 Memory::Object_at(padding_pointer) =
1418 Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
1419
1420 StackFrame* pre_pre_frame = frames[top_frame_index - 2];
1421
1422 MemMove(padding_start + kPointerSize - shortage_bytes,
1423 padding_start + kPointerSize,
1424 FrameDropperFrameConstants::kFixedFrameSize - kPointerSize);
1425
1426 pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
1427 pre_pre_frame->SetCallerFp(pre_top_frame->fp());
1428 unused_stack_top -= shortage_bytes;
1429
1430 STATIC_ASSERT(sizeof(Address) == kPointerSize);
1431 top_frame_pc_address -= shortage_bytes / kPointerSize;
1432 } else {
1433 return "Not enough space for frame dropper frame";
1434 }
1435 }
1436
1437 // Committing now. After this point we should return only NULL value.
1438
1439 FixTryCatchHandler(pre_top_frame, bottom_js_frame);
1440 // Make sure FixTryCatchHandler is idempotent.
1441 DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
1442
1443 Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit();
1444 *top_frame_pc_address = code->entry();
1445 pre_top_frame->SetCallerFp(bottom_js_frame->fp());
1446
1447 SetUpFrameDropperFrame(bottom_js_frame, code);
1448
1449 for (Address a = unused_stack_top;
1450 a < unused_stack_bottom;
1451 a += kPointerSize) {
1452 Memory::Object_at(a) = Smi::kZero;
1453 }
1454
1455 return NULL;
1456 }
1457
1458
1459 // Describes a set of call frames that execute any of listed functions. 1209 // Describes a set of call frames that execute any of listed functions.
1460 // Finding no such frames does not mean error. 1210 // Finding no such frames does not mean error.
1461 class MultipleFunctionTarget { 1211 class MultipleFunctionTarget {
1462 public: 1212 public:
1463 MultipleFunctionTarget(Handle<JSArray> old_shared_array, 1213 MultipleFunctionTarget(Handle<JSArray> old_shared_array,
1464 Handle<JSArray> new_shared_array, 1214 Handle<JSArray> new_shared_array,
1465 Handle<JSArray> result) 1215 Handle<JSArray> result)
1466 : old_shared_array_(old_shared_array), 1216 : old_shared_array_(old_shared_array),
1467 new_shared_array_(new_shared_array), 1217 new_shared_array_(new_shared_array),
1468 result_(result) {} 1218 result_(result) {}
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1536 1286
1537 // Drops all call frame matched by target and all frames above them. 1287 // Drops all call frame matched by target and all frames above them.
1538 template <typename TARGET> 1288 template <typename TARGET>
1539 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, 1289 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
1540 TARGET& target, // NOLINT 1290 TARGET& target, // NOLINT
1541 bool do_drop) { 1291 bool do_drop) {
1542 Debug* debug = isolate->debug(); 1292 Debug* debug = isolate->debug();
1543 Zone zone(isolate->allocator(), ZONE_NAME); 1293 Zone zone(isolate->allocator(), ZONE_NAME);
1544 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone); 1294 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
1545 1295
1546
1547 int top_frame_index = -1; 1296 int top_frame_index = -1;
1548 int frame_index = 0; 1297 int frame_index = 0;
1549 for (; frame_index < frames.length(); frame_index++) { 1298 for (; frame_index < frames.length(); frame_index++) {
1550 StackFrame* frame = frames[frame_index]; 1299 StackFrame* frame = frames[frame_index];
1551 if (frame->id() == debug->break_frame_id()) { 1300 if (frame->id() == debug->break_frame_id()) {
1552 top_frame_index = frame_index; 1301 top_frame_index = frame_index;
1553 break; 1302 break;
1554 } 1303 }
1555 if (target.MatchActivation( 1304 if (target.MatchActivation(
1556 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { 1305 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
1621 if (!do_drop) { 1370 if (!do_drop) {
1622 // We are in check-only mode. 1371 // We are in check-only mode.
1623 return NULL; 1372 return NULL;
1624 } 1373 }
1625 1374
1626 if (!target_frame_found) { 1375 if (!target_frame_found) {
1627 // Nothing to drop. 1376 // Nothing to drop.
1628 return target.GetNotFoundMessage(); 1377 return target.GetNotFoundMessage();
1629 } 1378 }
1630 1379
1631 LiveEditFrameDropMode drop_mode = LIVE_EDIT_FRAMES_UNTOUCHED; 1380 if (!LiveEdit::kFrameDropperSupported) {
1632 const char* error_message = 1381 return "Stack manipulations are not supported in this architecture.";
1633 DropFrames(frames, top_frame_index, bottom_js_frame_index, &drop_mode);
1634
1635 if (error_message != NULL) {
1636 return error_message;
1637 } 1382 }
1638 1383
1639 // Adjust break_frame after some frames has been dropped. 1384 debug->ScheduleFrameRestart(frames[bottom_js_frame_index]);
1640 StackFrame::Id new_id = StackFrame::NO_ID;
1641 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
1642 if (frames[i]->type() == StackFrame::JAVA_SCRIPT ||
1643 frames[i]->type() == StackFrame::INTERPRETED) {
1644 new_id = frames[i]->id();
1645 break;
1646 }
1647 }
1648 debug->FramesHaveBeenDropped(new_id, drop_mode);
1649 return NULL; 1385 return NULL;
1650 } 1386 }
1651 1387
1652 1388
1653 // Fills result array with statuses of functions. Modifies the stack 1389 // Fills result array with statuses of functions. Modifies the stack
1654 // removing all listed function if possible and if do_drop is true. 1390 // removing all listed function if possible and if do_drop is true.
1655 static const char* DropActivationsInActiveThread( 1391 static const char* DropActivationsInActiveThread(
1656 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, 1392 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
1657 Handle<JSArray> result, bool do_drop) { 1393 Handle<JSArray> result, bool do_drop) {
1658 MultipleFunctionTarget target(old_shared_array, new_shared_array, result); 1394 MultipleFunctionTarget target(old_shared_array, new_shared_array, result);
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
1935 scope_info_length++; 1671 scope_info_length++;
1936 1672
1937 current_scope = current_scope->outer_scope(); 1673 current_scope = current_scope->outer_scope();
1938 } 1674 }
1939 1675
1940 return scope_info_list; 1676 return scope_info_list;
1941 } 1677 }
1942 1678
1943 } // namespace internal 1679 } // namespace internal
1944 } // namespace v8 1680 } // namespace v8
OLDNEW
« no previous file with comments | « src/debug/liveedit.h ('k') | src/debug/mips/debug-mips.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698