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

Side by Side Diff: src/liveedit.cc

Issue 1118007: LiveEdit: implement frame dropping (Closed)
Patch Set: adding rule to mjsunit.status Created 10 years, 8 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/liveedit.h ('k') | src/liveedit-debugger.js » ('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 2010 the V8 project authors. All rights reserved. 1 // Copyright 2010 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 16 matching lines...) Expand all
27 27
28 28
29 #include "v8.h" 29 #include "v8.h"
30 30
31 #include "liveedit.h" 31 #include "liveedit.h"
32 #include "compiler.h" 32 #include "compiler.h"
33 #include "oprofile-agent.h" 33 #include "oprofile-agent.h"
34 #include "scopes.h" 34 #include "scopes.h"
35 #include "global-handles.h" 35 #include "global-handles.h"
36 #include "debug.h" 36 #include "debug.h"
37 #include "memory.h"
37 38
38 namespace v8 { 39 namespace v8 {
39 namespace internal { 40 namespace internal {
40 41
41 42
42 #ifdef ENABLE_DEBUGGER_SUPPORT 43 #ifdef ENABLE_DEBUGGER_SUPPORT
43 44
44 static void CompileScriptForTracker(Handle<Script> script) { 45 static void CompileScriptForTracker(Handle<Script> script) {
45 const bool is_eval = false; 46 const bool is_eval = false;
46 const bool is_global = true; 47 const bool is_global = true;
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after
666 BreakPointInfo::cast(break_point_infos->get(i))); 667 BreakPointInfo::cast(break_point_infos->get(i)));
667 int new_position = TranslatePosition(info->source_position()->value(), 668 int new_position = TranslatePosition(info->source_position()->value(),
668 position_change_array); 669 position_change_array);
669 info->set_source_position(Smi::FromInt(new_position)); 670 info->set_source_position(Smi::FromInt(new_position));
670 } 671 }
671 } 672 }
672 // TODO(635): Also patch breakpoint objects in JS. 673 // TODO(635): Also patch breakpoint objects in JS.
673 } 674 }
674 675
675 676
677 // Check an activation against list of functions. If there is a function
678 // that matches, its status in result array is changed to status argument value.
679 static bool CheckActivation(Handle<JSArray> shared_info_array,
680 Handle<JSArray> result, StackFrame* frame,
681 LiveEdit::FunctionPatchabilityStatus status) {
682 if (!frame->is_java_script()) {
683 return false;
684 }
685 int len = Smi::cast(shared_info_array->length())->value();
686 for (int i = 0; i < len; i++) {
687 JSValue* wrapper = JSValue::cast(shared_info_array->GetElement(i));
688 Handle<SharedFunctionInfo> shared(
689 SharedFunctionInfo::cast(wrapper->value()));
690
691 if (frame->code() == shared->code()) {
692 SetElement(result, i, Handle<Smi>(Smi::FromInt(status)));
693 return true;
694 }
695 }
696 return false;
697 }
698
699
700 // Iterates over handler chain and removes all elements that are inside
701 // frames being dropped.
702 static bool FixTryCatchHandler(StackFrame* top_frame,
703 StackFrame* bottom_frame) {
704 Address* pointer_address =
705 &Memory::Address_at(Top::get_address_from_id(Top::k_handler_address));
706
707 while (*pointer_address < top_frame->sp()) {
708 pointer_address = &Memory::Address_at(*pointer_address);
709 }
710 Address* above_frame_address = pointer_address;
711 while (*pointer_address < bottom_frame->fp()) {
712 pointer_address = &Memory::Address_at(*pointer_address);
713 }
714 bool change = *above_frame_address != *pointer_address;
715 *above_frame_address = *pointer_address;
716 return change;
717 }
718
719
720 // Removes specified range of frames from stack. There may be 1 or more
721 // frames in range. Anyway the bottom frame is restarted rather than dropped,
722 // and therefore has to be a JavaScript frame.
723 // Returns error message or NULL.
724 static const char* DropFrames(Vector<StackFrame*> frames,
725 int top_frame_index,
726 int bottom_js_frame_index) {
727 StackFrame* pre_top_frame = frames[top_frame_index - 1];
728 StackFrame* top_frame = frames[top_frame_index];
729 StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
730
731 ASSERT(bottom_js_frame->is_java_script());
732
733 // Check the nature of the top frame.
734 if (pre_top_frame->code()->is_inline_cache_stub() &&
735 pre_top_frame->code()->ic_state() == DEBUG_BREAK) {
736 // OK, we can drop inline cache calls.
737 } else if (pre_top_frame->code() ==
738 Builtins::builtin(Builtins::FrameDropper_LiveEdit)) {
739 // OK, we can drop our own code.
740 } else if (pre_top_frame->code()->kind() == Code::STUB &&
741 pre_top_frame->code()->major_key()) {
742 // Unit Test entry, it's fine, we support this case.
743 } else {
744 return "Unknown structure of stack above changing function";
745 }
746
747 Address unused_stack_top = top_frame->sp();
748 Address unused_stack_bottom = bottom_js_frame->fp()
749 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
750 + kPointerSize; // Bigger address end is exclusive.
751
752 if (unused_stack_top > unused_stack_bottom) {
753 return "Not enough space for frame dropper frame";
754 }
755
756 // Committing now. After this point we should return only NULL value.
757
758 FixTryCatchHandler(pre_top_frame, bottom_js_frame);
759 // Make sure FixTryCatchHandler is idempotent.
760 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
761
762 Handle<Code> code(Builtins::builtin(Builtins::FrameDropper_LiveEdit));
763 top_frame->set_pc(code->entry());
764 pre_top_frame->SetCallerFp(bottom_js_frame->fp());
765
766 Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
767
768 for (Address a = unused_stack_top;
769 a < unused_stack_bottom;
770 a += kPointerSize) {
771 Memory::Object_at(a) = Smi::FromInt(0);
772 }
773
774 return NULL;
775 }
776
777
778 static bool IsDropableFrame(StackFrame* frame) {
779 return !frame->is_exit();
780 }
781
782 // Fills result array with statuses of functions. Modifies the stack
783 // removing all listed function if possible and if do_drop is true.
784 static const char* DropActivationsInActiveThread(
785 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
786
787 ZoneScope scope(DELETE_ON_EXIT);
788 Vector<StackFrame*> frames = CreateStackMap();
789
790 int array_len = Smi::cast(shared_info_array->length())->value();
791
792 int top_frame_index = -1;
793 int frame_index = 0;
794 for (; frame_index < frames.length(); frame_index++) {
795 StackFrame* frame = frames[frame_index];
796 if (frame->id() == Debug::break_frame_id()) {
797 top_frame_index = frame_index;
798 break;
799 }
800 if (CheckActivation(shared_info_array, result, frame,
801 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
802 // We are still above break_frame. It is not a target frame,
803 // it is a problem.
804 return "Debugger mark-up on stack is not found";
805 }
806 }
807
808 if (top_frame_index == -1) {
809 // We haven't found break frame, but no function is blocking us anyway.
810 return NULL;
811 }
812
813 bool target_frame_found = false;
814 int bottom_js_frame_index = top_frame_index;
815 bool c_code_found = false;
816
817 for (; frame_index < frames.length(); frame_index++) {
818 StackFrame* frame = frames[frame_index];
819 if (!IsDropableFrame(frame)) {
820 c_code_found = true;
821 break;
822 }
823 if (CheckActivation(shared_info_array, result, frame,
824 LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
825 target_frame_found = true;
826 bottom_js_frame_index = frame_index;
827 }
828 }
829
830 if (c_code_found) {
831 // There is a C frames on stack. Check that there are no target frames
832 // below them.
833 for (; frame_index < frames.length(); frame_index++) {
834 StackFrame* frame = frames[frame_index];
835 if (frame->is_java_script()) {
836 if (CheckActivation(shared_info_array, result, frame,
837 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
838 // Cannot drop frame under C frames.
839 return NULL;
840 }
841 }
842 }
843 }
844
845 if (!do_drop) {
846 // We are in check-only mode.
847 return NULL;
848 }
849
850 if (!target_frame_found) {
851 // Nothing to drop.
852 return NULL;
853 }
854
855 const char* error_message = DropFrames(frames, top_frame_index,
856 bottom_js_frame_index);
857
858 if (error_message != NULL) {
859 return error_message;
860 }
861
862 // Adjust break_frame after some frames has been dropped.
863 StackFrame::Id new_id = StackFrame::NO_ID;
864 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
865 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
866 new_id = frames[i]->id();
867 break;
868 }
869 }
870 Debug::FramesHaveBeenDropped(new_id);
871
872 // Replace "blocked on active" with "replaced on active" status.
873 for (int i = 0; i < array_len; i++) {
874 if (result->GetElement(i) ==
875 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
876 result->SetElement(i, Smi::FromInt(
877 LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK));
878 }
879 }
880 return NULL;
881 }
882
883
884 class InactiveThreadActivationsChecker : public ThreadVisitor {
885 public:
886 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
887 Handle<JSArray> result)
888 : shared_info_array_(shared_info_array), result_(result),
889 has_blocked_functions_(false) {
890 }
891 void VisitThread(ThreadLocalTop* top) {
892 for (StackFrameIterator it(top); !it.done(); it.Advance()) {
893 has_blocked_functions_ |= CheckActivation(
894 shared_info_array_, result_, it.frame(),
895 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
896 }
897 }
898 bool HasBlockedFunctions() {
899 return has_blocked_functions_;
900 }
901
902 private:
903 Handle<JSArray> shared_info_array_;
904 Handle<JSArray> result_;
905 bool has_blocked_functions_;
906 };
907
908
909 Handle<JSArray> LiveEdit::CheckAndDropActivations(
910 Handle<JSArray> shared_info_array, bool do_drop) {
911 int len = Smi::cast(shared_info_array->length())->value();
912
913 Handle<JSArray> result = Factory::NewJSArray(len);
914
915 // Fill the default values.
916 for (int i = 0; i < len; i++) {
917 SetElement(result, i,
918 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH)));
919 }
920
921
922 // First check inactive threads. Fail if some functions are blocked there.
923 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
924 result);
925 ThreadManager::IterateThreads(&inactive_threads_checker);
926 if (inactive_threads_checker.HasBlockedFunctions()) {
927 return result;
928 }
929
930 // Try to drop activations from the current stack.
931 const char* error_message =
932 DropActivationsInActiveThread(shared_info_array, result, do_drop);
933 if (error_message != NULL) {
934 // Add error message as an array extra element.
935 Vector<const char> vector_message(error_message, strlen(error_message));
936 Handle<String> str = Factory::NewStringFromAscii(vector_message);
937 SetElement(result, len, str);
938 }
939 return result;
940 }
941
942
676 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { 943 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
677 if (active_function_info_listener != NULL) { 944 if (active_function_info_listener != NULL) {
678 active_function_info_listener->FunctionStarted(fun); 945 active_function_info_listener->FunctionStarted(fun);
679 } 946 }
680 } 947 }
681 948
682 949
683 LiveEditFunctionTracker::~LiveEditFunctionTracker() { 950 LiveEditFunctionTracker::~LiveEditFunctionTracker() {
684 if (active_function_info_listener != NULL) { 951 if (active_function_info_listener != NULL) {
685 active_function_info_listener->FunctionDone(); 952 active_function_info_listener->FunctionDone();
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
728 995
729 bool LiveEditFunctionTracker::IsActive() { 996 bool LiveEditFunctionTracker::IsActive() {
730 return false; 997 return false;
731 } 998 }
732 999
733 #endif // ENABLE_DEBUGGER_SUPPORT 1000 #endif // ENABLE_DEBUGGER_SUPPORT
734 1001
735 1002
736 1003
737 } } // namespace v8::internal 1004 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-debugger.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698