From bbc2ac93a305f4ea223a894f0f6ab4083ee06ce9 Mon Sep 17 00:00:00 2001 From: Todd Turnidge Date: Tue, 31 Jan 2017 12:02:26 -0800 Subject: [PATCH] Start adding vm/cc tests for rewind functionality. These will make it easier to debug rewind on simdbc64. BUG= R=rmacnak@google.com Review-Url: https://codereview.chromium.org/2662333002 . --- runtime/tests/vm/vm.status | 4 + runtime/vm/dart_api_message.cc | 2 +- runtime/vm/debugger_test.cc | 204 +++++++++++++++++++++++++++++++++ runtime/vm/json_stream.cc | 17 ++- runtime/vm/service.cc | 11 +- 5 files changed, 230 insertions(+), 8 deletions(-) diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index a3355b03d04..c259658ea11 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -299,3 +299,7 @@ dart/spawn_infinite_loop_test: Skip # We can shutdown an isolate before it reloa [ $system == linux ] # Tests are currently flaky and are being rewritten. cc/BasicMallocHookTest: Pass,Fail cc/FreeUnseenMemoryMallocHookTest: Pass,Fail + +[ $arch == simdbc || $arch == simdbc64 ] +cc/Debugger_RewindOneFrame_Unoptimized: Skip # Issue 27878 +cc/Debugger_RewindTwoFrames_Unoptimized: Skip # Issue 27878 diff --git a/runtime/vm/dart_api_message.cc b/runtime/vm/dart_api_message.cc index 6e1afc487b4..60985b1dcaa 100644 --- a/runtime/vm/dart_api_message.cc +++ b/runtime/vm/dart_api_message.cc @@ -1300,7 +1300,7 @@ bool ApiMessageWriter::WriteCObjectInlined(Dart_CObject* object, break; } default: - UNREACHABLE(); + FATAL1("Unexpected Dart_CObject_Type %d\n", type); } return true; diff --git a/runtime/vm/debugger_test.cc b/runtime/vm/debugger_test.cc index 60969659852..233211cdf96 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(ptr), new_size); + return reinterpret_cast(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("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("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(isolate->main_port())); + param0_value.value.as_string = const_cast(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("step"); + param_keys_list[1] = ¶m1_name; + + Dart_CObject param1_value; + param1_value.type = Dart_CObject_kString; + param1_value.value.as_string = const_cast("Rewind"); + param_values_list[1] = ¶m1_value; + + Dart_CObject param2_name; + param2_name.type = Dart_CObject_kString; + param2_name.value.as_string = const_cast("frameIndex"); + param_keys_list[2] = ¶m2_name; + + Dart_CObject param2_value; + param2_value.type = Dart_CObject_kString; + param2_value.value.as_string = const_cast(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 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 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 diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc index f098998dd16..0f041174a8d 100644 --- a/runtime/vm/json_stream.cc +++ b/runtime/vm/json_stream.cc @@ -210,10 +210,9 @@ static void Finalizer(void* isolate_callback_data, void JSONStream::PostReply() { - Dart_Port port = reply_port(); - ASSERT(port != ILLEGAL_PORT); - set_reply_port(ILLEGAL_PORT); // Prevent double replies. ASSERT(seq_ != NULL); + Dart_Port port = reply_port(); + set_reply_port(ILLEGAL_PORT); // Prevent double replies. if (seq_->IsString()) { const String& str = String::Cast(*seq_); PrintProperty("id", str.ToCString()); @@ -224,12 +223,22 @@ void JSONStream::PostReply() { const Double& dbl = Double::Cast(*seq_); PrintProperty("id", dbl.value()); } else if (seq_->IsNull()) { + if (port == ILLEGAL_PORT) { + // This path is only used in tests. + buffer_.AddChar('}'); // Finish our message. + char* cstr; + intptr_t length; + Steal(&cstr, &length); + OS::PrintErr("-----\nDropping reply:\n%s\n-----\n", cstr); + free(cstr); + } // JSON-RPC 2.0 says that a request with a null ID shouldn't get a reply. PostNullReply(port); return; } - buffer_.AddChar('}'); + ASSERT(port != ILLEGAL_PORT); + buffer_.AddChar('}'); // Finish our message. char* cstr; intptr_t length; Steal(&cstr, &length); diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index bdc71e989bc..82946b6b711 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -840,13 +840,18 @@ void Service::InvokeMethod(Isolate* I, ASSERT(!param_values.IsNull()); ASSERT(param_keys.Length() == param_values.Length()); - if (!reply_port.IsSendPort()) { + // We expect a reply port unless there is a null sequence id, + // which indicates that no reply should be sent. We use this in + // tests. + if (!seq.IsNull() && !reply_port.IsSendPort()) { FATAL("SendPort expected."); } JSONStream js; - js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), seq, method_name, - param_keys, param_values, parameters_are_dart_objects); + Dart_Port reply_port_id = + (reply_port.IsNull() ? ILLEGAL_PORT : SendPort::Cast(reply_port).Id()); + js.Setup(zone.GetZone(), reply_port_id, seq, method_name, param_keys, + param_values, parameters_are_dart_objects); // RPC came in with a custom service id zone. const char* id_zone_param = js.LookupParam("_idZone");