| Index: runtime/vm/debugger_test.cc
|
| diff --git a/runtime/vm/debugger_test.cc b/runtime/vm/debugger_test.cc
|
| index 6096965985244452b1ae141daa5e00e3a1905068..233211cdf968156e047d91c90b76883f15b0b683 100644
|
| --- a/runtime/vm/debugger_test.cc
|
| +++ b/runtime/vm/debugger_test.cc
|
| @@ -3,7 +3,9 @@
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| #include "vm/dart_api_impl.h"
|
| +#include "vm/dart_api_message.h"
|
| #include "vm/debugger.h"
|
| +#include "vm/message.h"
|
| #include "vm/unit_test.h"
|
|
|
| namespace dart {
|
| @@ -11,6 +13,7 @@ namespace dart {
|
| #ifndef PRODUCT
|
|
|
| DECLARE_FLAG(bool, remove_script_timestamps_for_test);
|
| +DECLARE_FLAG(bool, trace_rewind);
|
|
|
| // Search for the formatted string in buffer.
|
| //
|
| @@ -141,6 +144,207 @@ TEST_CASE(Debugger_PauseEvent) {
|
| EXPECT(saw_paused_event);
|
| }
|
|
|
| +
|
| +static uint8_t* malloc_allocator(uint8_t* ptr,
|
| + intptr_t old_size,
|
| + intptr_t new_size) {
|
| + void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size);
|
| + return reinterpret_cast<uint8_t*>(new_ptr);
|
| +}
|
| +
|
| +
|
| +const char* rewind_frame_index = "-1";
|
| +
|
| +
|
| +// Build and send a fake resume OOB message for testing purposes.
|
| +void SendResumeMessage(Isolate* isolate) {
|
| + // Format is: [ oob_type, port, seq, method_name, [keys], [values] ]
|
| + Dart_CObject msg;
|
| + Dart_CObject* list_values[6];
|
| + msg.type = Dart_CObject_kArray;
|
| + msg.value.as_array.length = 6;
|
| + msg.value.as_array.values = list_values;
|
| +
|
| + Dart_CObject oob;
|
| + oob.type = Dart_CObject_kInt32;
|
| + oob.value.as_int32 = Message::kServiceOOBMsg;
|
| + list_values[0] = &oob;
|
| +
|
| + Dart_CObject reply_port;
|
| + reply_port.type = Dart_CObject_kNull;
|
| + list_values[1] = &reply_port;
|
| +
|
| + Dart_CObject seq;
|
| + seq.type = Dart_CObject_kNull;
|
| + list_values[2] = &seq;
|
| +
|
| + Dart_CObject method_name;
|
| + method_name.type = Dart_CObject_kString;
|
| + method_name.value.as_string = const_cast<char*>("resume");
|
| + list_values[3] = &method_name;
|
| +
|
| + const int kParamCount = 3;
|
| + Dart_CObject param_keys;
|
| + Dart_CObject* param_keys_list[kParamCount];
|
| + param_keys.type = Dart_CObject_kArray;
|
| + param_keys.value.as_array.values = param_keys_list;
|
| + param_keys.value.as_array.length = kParamCount;
|
| + list_values[4] = ¶m_keys;
|
| +
|
| + Dart_CObject param_values;
|
| + Dart_CObject* param_values_list[kParamCount];
|
| + param_values.type = Dart_CObject_kArray;
|
| + param_values.value.as_array.values = param_values_list;
|
| + param_values.value.as_array.length = kParamCount;
|
| + list_values[5] = ¶m_values;
|
| +
|
| + Dart_CObject param0_name;
|
| + param0_name.type = Dart_CObject_kString;
|
| + param0_name.value.as_string = const_cast<char*>("isolateId");
|
| + param_keys_list[0] = ¶m0_name;
|
| +
|
| + Dart_CObject param0_value;
|
| + param0_value.type = Dart_CObject_kString;
|
| + const char* isolate_id = Thread::Current()->zone()->PrintToString(
|
| + ISOLATE_SERVICE_ID_FORMAT_STRING,
|
| + static_cast<int64_t>(isolate->main_port()));
|
| + param0_value.value.as_string = const_cast<char*>(isolate_id);
|
| + param_values_list[0] = ¶m0_value;
|
| +
|
| + Dart_CObject param1_name;
|
| + param1_name.type = Dart_CObject_kString;
|
| + param1_name.value.as_string = const_cast<char*>("step");
|
| + param_keys_list[1] = ¶m1_name;
|
| +
|
| + Dart_CObject param1_value;
|
| + param1_value.type = Dart_CObject_kString;
|
| + param1_value.value.as_string = const_cast<char*>("Rewind");
|
| + param_values_list[1] = ¶m1_value;
|
| +
|
| + Dart_CObject param2_name;
|
| + param2_name.type = Dart_CObject_kString;
|
| + param2_name.value.as_string = const_cast<char*>("frameIndex");
|
| + param_keys_list[2] = ¶m2_name;
|
| +
|
| + Dart_CObject param2_value;
|
| + param2_value.type = Dart_CObject_kString;
|
| + param2_value.value.as_string = const_cast<char*>(rewind_frame_index);
|
| + param_values_list[2] = ¶m2_value;
|
| +
|
| + {
|
| + uint8_t* buffer = NULL;
|
| + ApiMessageWriter writer(&buffer, &malloc_allocator);
|
| + bool success = writer.WriteCMessage(&msg);
|
| + ASSERT(success);
|
| +
|
| + // Post the message at the given port.
|
| + success = PortMap::PostMessage(new Message(isolate->main_port(), buffer,
|
| + writer.BytesWritten(),
|
| + Message::kOOBPriority));
|
| + ASSERT(success);
|
| + }
|
| +}
|
| +
|
| +
|
| +static void RewindOnce(Dart_IsolateId isolate_id,
|
| + intptr_t bp_id,
|
| + const Dart_CodeLocation& loc) {
|
| + bool first_time = !saw_paused_event;
|
| + saw_paused_event = true;
|
| + if (first_time) {
|
| + Thread* T = Thread::Current();
|
| + Isolate* I = T->isolate();
|
| + // TODO(turnidge): It is weird that the isolate can get to this
|
| + // point in our tests without being marked runnable. Clear this up
|
| + // at some point.
|
| + I->set_is_runnable(true);
|
| + SendResumeMessage(I);
|
| + I->PauseEventHandler();
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST_CASE(Debugger_RewindOneFrame_Unoptimized) {
|
| + SetFlagScope<bool> sfs(&FLAG_trace_rewind, true);
|
| + saw_paused_event = false;
|
| + rewind_frame_index = "1";
|
| +
|
| + const char* kScriptChars =
|
| + "import 'dart:developer';\n"
|
| + "\n"
|
| + "var msg = new StringBuffer();\n"
|
| + "\n"
|
| + "foo() {\n"
|
| + " msg.write('enter(foo) ');\n"
|
| + " debugger();\n"
|
| + " msg.write('exit(foo) ');\n"
|
| + "}\n"
|
| + "\n"
|
| + "main() {\n"
|
| + " msg.write('enter(main) ');\n"
|
| + " foo();\n"
|
| + " msg.write('exit(main) ');\n"
|
| + " return msg.toString();\n"
|
| + "}\n";
|
| + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
|
| + EXPECT_VALID(lib);
|
| +
|
| + Dart_SetPausedEventHandler(RewindOnce);
|
| + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
| + const char* result_cstr;
|
| + EXPECT_VALID(result);
|
| + EXPECT(Dart_IsString(result));
|
| + EXPECT_VALID(Dart_StringToCString(result, &result_cstr));
|
| + EXPECT_STREQ("enter(main) enter(foo) enter(foo) exit(foo) exit(main) ",
|
| + result_cstr);
|
| + EXPECT(saw_paused_event);
|
| +}
|
| +
|
| +
|
| +TEST_CASE(Debugger_RewindTwoFrames_Unoptimized) {
|
| + SetFlagScope<bool> sfs(&FLAG_trace_rewind, true);
|
| + saw_paused_event = false;
|
| + rewind_frame_index = "2";
|
| +
|
| + const char* kScriptChars =
|
| + "import 'dart:developer';\n"
|
| + "\n"
|
| + "var msg = new StringBuffer();\n"
|
| + "\n"
|
| + "foo() {\n"
|
| + " msg.write('enter(foo) ');\n"
|
| + " debugger();\n"
|
| + " msg.write('exit(foo) ');\n"
|
| + "}\n"
|
| + "\n"
|
| + "bar() {\n"
|
| + " msg.write('enter(bar) ');\n"
|
| + " foo();\n"
|
| + " msg.write('exit(bar) ');\n"
|
| + "}\n"
|
| + "\n"
|
| + "main() {\n"
|
| + " msg.write('enter(main) ');\n"
|
| + " bar();\n"
|
| + " msg.write('exit(main) ');\n"
|
| + " return msg.toString();\n"
|
| + "}\n";
|
| + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
|
| + EXPECT_VALID(lib);
|
| +
|
| + Dart_SetPausedEventHandler(RewindOnce);
|
| + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
| + const char* result_cstr;
|
| + EXPECT_VALID(result);
|
| + EXPECT(Dart_IsString(result));
|
| + EXPECT_VALID(Dart_StringToCString(result, &result_cstr));
|
| + EXPECT_STREQ(
|
| + "enter(main) enter(bar) enter(foo) enter(bar) enter(foo) "
|
| + "exit(foo) exit(bar) exit(main) ",
|
| + result_cstr);
|
| + EXPECT(saw_paused_event);
|
| +}
|
| +
|
| #endif // !PRODUCT
|
|
|
| } // namespace dart
|
|
|