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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-debugger.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/liveedit.cc
diff --git a/src/liveedit.cc b/src/liveedit.cc
index 55308ab67ce4d1043046618351b57295c929e640..0e836e5f500333b9944eb30fb7c4b83491ca2a58 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -34,6 +34,7 @@
#include "scopes.h"
#include "global-handles.h"
#include "debug.h"
+#include "memory.h"
namespace v8 {
namespace internal {
@@ -673,6 +674,272 @@ void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
}
+// Check an activation against list of functions. If there is a function
+// that matches, its status in result array is changed to status argument value.
+static bool CheckActivation(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result, StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ if (!frame->is_java_script()) {
+ return false;
+ }
+ int len = Smi::cast(shared_info_array->length())->value();
+ for (int i = 0; i < len; i++) {
+ JSValue* wrapper = JSValue::cast(shared_info_array->GetElement(i));
+ Handle<SharedFunctionInfo> shared(
+ SharedFunctionInfo::cast(wrapper->value()));
+
+ if (frame->code() == shared->code()) {
+ SetElement(result, i, Handle<Smi>(Smi::FromInt(status)));
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Iterates over handler chain and removes all elements that are inside
+// frames being dropped.
+static bool FixTryCatchHandler(StackFrame* top_frame,
+ StackFrame* bottom_frame) {
+ Address* pointer_address =
+ &Memory::Address_at(Top::get_address_from_id(Top::k_handler_address));
+
+ while (*pointer_address < top_frame->sp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ Address* above_frame_address = pointer_address;
+ while (*pointer_address < bottom_frame->fp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ bool change = *above_frame_address != *pointer_address;
+ *above_frame_address = *pointer_address;
+ return change;
+}
+
+
+// Removes specified range of frames from stack. There may be 1 or more
+// frames in range. Anyway the bottom frame is restarted rather than dropped,
+// and therefore has to be a JavaScript frame.
+// Returns error message or NULL.
+static const char* DropFrames(Vector<StackFrame*> frames,
+ int top_frame_index,
+ int bottom_js_frame_index) {
+ StackFrame* pre_top_frame = frames[top_frame_index - 1];
+ StackFrame* top_frame = frames[top_frame_index];
+ StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
+
+ ASSERT(bottom_js_frame->is_java_script());
+
+ // Check the nature of the top frame.
+ if (pre_top_frame->code()->is_inline_cache_stub() &&
+ pre_top_frame->code()->ic_state() == DEBUG_BREAK) {
+ // OK, we can drop inline cache calls.
+ } else if (pre_top_frame->code() ==
+ Builtins::builtin(Builtins::FrameDropper_LiveEdit)) {
+ // OK, we can drop our own code.
+ } else if (pre_top_frame->code()->kind() == Code::STUB &&
+ pre_top_frame->code()->major_key()) {
+ // Unit Test entry, it's fine, we support this case.
+ } else {
+ return "Unknown structure of stack above changing function";
+ }
+
+ Address unused_stack_top = top_frame->sp();
+ Address unused_stack_bottom = bottom_js_frame->fp()
+ - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
+ + kPointerSize; // Bigger address end is exclusive.
+
+ if (unused_stack_top > unused_stack_bottom) {
+ return "Not enough space for frame dropper frame";
+ }
+
+ // Committing now. After this point we should return only NULL value.
+
+ FixTryCatchHandler(pre_top_frame, bottom_js_frame);
+ // Make sure FixTryCatchHandler is idempotent.
+ ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
+
+ Handle<Code> code(Builtins::builtin(Builtins::FrameDropper_LiveEdit));
+ top_frame->set_pc(code->entry());
+ pre_top_frame->SetCallerFp(bottom_js_frame->fp());
+
+ Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
+
+ for (Address a = unused_stack_top;
+ a < unused_stack_bottom;
+ a += kPointerSize) {
+ Memory::Object_at(a) = Smi::FromInt(0);
+ }
+
+ return NULL;
+}
+
+
+static bool IsDropableFrame(StackFrame* frame) {
+ return !frame->is_exit();
+}
+
+// Fills result array with statuses of functions. Modifies the stack
+// removing all listed function if possible and if do_drop is true.
+static const char* DropActivationsInActiveThread(
+ Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
+
+ ZoneScope scope(DELETE_ON_EXIT);
+ Vector<StackFrame*> frames = CreateStackMap();
+
+ int array_len = Smi::cast(shared_info_array->length())->value();
+
+ int top_frame_index = -1;
+ int frame_index = 0;
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->id() == Debug::break_frame_id()) {
+ top_frame_index = frame_index;
+ break;
+ }
+ if (CheckActivation(shared_info_array, result, frame,
+ LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
+ // We are still above break_frame. It is not a target frame,
+ // it is a problem.
+ return "Debugger mark-up on stack is not found";
+ }
+ }
+
+ if (top_frame_index == -1) {
+ // We haven't found break frame, but no function is blocking us anyway.
+ return NULL;
+ }
+
+ bool target_frame_found = false;
+ int bottom_js_frame_index = top_frame_index;
+ bool c_code_found = false;
+
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (!IsDropableFrame(frame)) {
+ c_code_found = true;
+ break;
+ }
+ if (CheckActivation(shared_info_array, result, frame,
+ LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ target_frame_found = true;
+ bottom_js_frame_index = frame_index;
+ }
+ }
+
+ if (c_code_found) {
+ // There is a C frames on stack. Check that there are no target frames
+ // below them.
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->is_java_script()) {
+ if (CheckActivation(shared_info_array, result, frame,
+ LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
+ // Cannot drop frame under C frames.
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!do_drop) {
+ // We are in check-only mode.
+ return NULL;
+ }
+
+ if (!target_frame_found) {
+ // Nothing to drop.
+ return NULL;
+ }
+
+ const char* error_message = DropFrames(frames, top_frame_index,
+ bottom_js_frame_index);
+
+ if (error_message != NULL) {
+ return error_message;
+ }
+
+ // Adjust break_frame after some frames has been dropped.
+ StackFrame::Id new_id = StackFrame::NO_ID;
+ for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
+ if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
+ new_id = frames[i]->id();
+ break;
+ }
+ }
+ Debug::FramesHaveBeenDropped(new_id);
+
+ // Replace "blocked on active" with "replaced on active" status.
+ for (int i = 0; i < array_len; i++) {
+ if (result->GetElement(i) ==
+ Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ result->SetElement(i, Smi::FromInt(
+ LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK));
+ }
+ }
+ return NULL;
+}
+
+
+class InactiveThreadActivationsChecker : public ThreadVisitor {
+ public:
+ InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result)
+ : shared_info_array_(shared_info_array), result_(result),
+ has_blocked_functions_(false) {
+ }
+ void VisitThread(ThreadLocalTop* top) {
+ for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ has_blocked_functions_ |= CheckActivation(
+ shared_info_array_, result_, it.frame(),
+ LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
+ }
+ }
+ bool HasBlockedFunctions() {
+ return has_blocked_functions_;
+ }
+
+ private:
+ Handle<JSArray> shared_info_array_;
+ Handle<JSArray> result_;
+ bool has_blocked_functions_;
+};
+
+
+Handle<JSArray> LiveEdit::CheckAndDropActivations(
+ Handle<JSArray> shared_info_array, bool do_drop) {
+ int len = Smi::cast(shared_info_array->length())->value();
+
+ Handle<JSArray> result = Factory::NewJSArray(len);
+
+ // Fill the default values.
+ for (int i = 0; i < len; i++) {
+ SetElement(result, i,
+ Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH)));
+ }
+
+
+ // First check inactive threads. Fail if some functions are blocked there.
+ InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
+ result);
+ ThreadManager::IterateThreads(&inactive_threads_checker);
+ if (inactive_threads_checker.HasBlockedFunctions()) {
+ return result;
+ }
+
+ // Try to drop activations from the current stack.
+ const char* error_message =
+ DropActivationsInActiveThread(shared_info_array, result, do_drop);
+ if (error_message != NULL) {
+ // Add error message as an array extra element.
+ Vector<const char> vector_message(error_message, strlen(error_message));
+ Handle<String> str = Factory::NewStringFromAscii(vector_message);
+ SetElement(result, len, str);
+ }
+ return result;
+}
+
+
LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
if (active_function_info_listener != NULL) {
active_function_info_listener->FunctionStarted(fun);
« 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