[dart][crash] send unhandled exceptions to crash analyzer

DX-246 #comment

TESTED=`run fuchsia-pkg://fuchsia.com/crasher_dart#meta/crasher_dart.cmx crasher_dart async` (report id 97d276698a970ec5)

Change-Id: I97cd2852de673007af9c0ba8907411739e7a7649
diff --git a/runtime/dart/utils/BUILD.gn b/runtime/dart/utils/BUILD.gn
new file mode 100644
index 0000000..48ee3f4
--- /dev/null
+++ b/runtime/dart/utils/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("utils") {
+  sources = [
+    "handle_exception.cc",
+    "handle_exception.h",
+  ]
+
+  public_deps = [
+    "//garnet/public/lib/component/cpp",
+    "//zircon/public/lib/zx",
+  ]
+
+  deps = [
+    "//garnet/public/lib/fsl",
+    "//garnet/public/lib/fxl",
+    "//third_party/tonic",
+    "//zircon/public/fidl/fuchsia-crash",
+    "//zircon/public/fidl/fuchsia-mem",
+  ]
+}
diff --git a/runtime/dart/utils/handle_exception.cc b/runtime/dart/utils/handle_exception.cc
new file mode 100644
index 0000000..2c1eb38
--- /dev/null
+++ b/runtime/dart/utils/handle_exception.cc
@@ -0,0 +1,59 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "handle_exception.h"
+
+#include <string>
+
+#include <fuchsia/crash/cpp/fidl.h>
+#include <fuchsia/mem/cpp/fidl.h>
+#include <lib/fsl/vmo/strings.h>
+#include <lib/fxl/logging.h>
+#include <sys/types.h>
+#include <third_party/tonic/converter/dart_converter.h>
+#include <zircon/errors.h>
+#include <zircon/status.h>
+
+namespace fuchsia {
+namespace dart {
+
+zx_status_t HandleIfException(component::StartupContext* context,
+                              const std::string& component_url,
+                              Dart_Handle result) {
+  if (!Dart_IsError(result) || !Dart_ErrorHasException(result)) {
+    return ZX_OK;
+  }
+
+  const std::string error =
+      tonic::StdStringFromDart(Dart_ToString(Dart_ErrorGetException(result)));
+  fuchsia::mem::Buffer stack_trace;
+  if (!fsl::VmoFromString(tonic::StdStringFromDart(
+                              Dart_ToString(Dart_ErrorGetStackTrace(result))),
+                          &stack_trace)) {
+    FXL_LOG(ERROR) << "failed to convert Dart stack trace to VMO";
+    return ZX_ERR_INTERNAL;
+  }
+
+  fuchsia::crash::AnalyzerSyncPtr analyzer;
+  context->ConnectToEnvironmentService(analyzer.NewRequest());
+  FXL_DCHECK(analyzer);
+
+  zx_status_t out_status;
+  const zx_status_t status = analyzer->HandleManagedRuntimeException(
+      fuchsia::crash::ManagedRuntimeLanguage::DART, component_url, error,
+      std::move(stack_trace), &out_status);
+  if (status != ZX_OK) {
+    FXL_LOG(ERROR) << "failed to connect to crash analyzer: " << status << " ("
+                   << zx_status_get_string(status) << ")";
+    return ZX_ERR_INTERNAL;
+  } else if (out_status != ZX_OK) {
+    FXL_LOG(ERROR) << "failed to handle Dart exception: " << out_status << " ("
+                   << zx_status_get_string(out_status) << ")";
+    return ZX_ERR_INTERNAL;
+  }
+  return ZX_OK;
+}
+
+}  // namespace dart
+}  // namespace fuchsia
diff --git a/runtime/dart/utils/handle_exception.h b/runtime/dart/utils/handle_exception.h
new file mode 100644
index 0000000..b5766ca
--- /dev/null
+++ b/runtime/dart/utils/handle_exception.h
@@ -0,0 +1,28 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOPAZ_RUNTIME_DART_UTILS_HANDLE_EXCEPTION_H_
+#define TOPAZ_RUNTIME_DART_UTILS_HANDLE_EXCEPTION_H_
+
+#include <string>
+
+#include <lib/component/cpp/startup_context.h>
+#include <sys/types.h>
+#include <third_party/dart/runtime/include/dart_api.h>
+
+namespace fuchsia {
+namespace dart {
+
+// If |result| is a Dart Exception, passes the exception message and stack trace
+// to the crash analyzer service for further handling.
+//
+// Otherwise early returns with OK status.
+zx_status_t HandleIfException(component::StartupContext* context,
+                              const std::string& component_url,
+                              Dart_Handle result);
+
+}  // namespace dart
+}  // namespace fuchsia
+
+#endif  // TOPAZ_RUNTIME_DART_UTILS_HANDLE_EXCEPTION_H_
diff --git a/runtime/dart_runner/BUILD.gn b/runtime/dart_runner/BUILD.gn
index 8ccdba0..a376230 100644
--- a/runtime/dart_runner/BUILD.gn
+++ b/runtime/dart_runner/BUILD.gn
@@ -48,6 +48,7 @@
              "//garnet/public/lib/fxl",
              "//garnet/public/lib/svc/cpp",
              "//topaz/lib/deprecated_loop",
+             "//topaz/runtime/dart/utils",
              "//third_party/tonic",
              "//topaz/public/dart-pkg/fuchsia",
              "//zircon/public/lib/trace-provider",
@@ -124,7 +125,8 @@
     "//third_party/dart/runtime/observatory:observatory_archive"
 observatory_archive_dir = get_label_info(observatory_target, "target_gen_dir")
 observatory_archive_name = get_label_info(observatory_target, "name")
-observatory_archive_file = "${observatory_archive_dir}/${observatory_archive_name}.tar"
+observatory_archive_file =
+    "${observatory_archive_dir}/${observatory_archive_name}.tar"
 
 template("aot_runner_package") {
   assert(defined(invoker.product), "The parameter 'product' must be defined")
@@ -210,9 +212,7 @@
       "kernel:kernel_core_snapshot${product_suffix}",
     ]
     if (!invoker.product) {
-      deps += [
-        observatory_target,
-      ]
+      deps += [ observatory_target ]
     }
 
     binary = "dart_${jit_or_dbc}${product_suffix}_runner"
diff --git a/runtime/dart_runner/builtin_libraries.cc b/runtime/dart_runner/builtin_libraries.cc
index 5bfab87..29929f9 100644
--- a/runtime/dart_runner/builtin_libraries.cc
+++ b/runtime/dart_runner/builtin_libraries.cc
@@ -10,11 +10,11 @@
 #include "dart-pkg/fuchsia/sdk_ext/fuchsia.h"
 #include "lib/fxl/arraysize.h"
 #include "lib/fxl/logging.h"
+#include "third_party/dart/runtime/bin/io_natives.h"
+#include "third_party/dart/runtime/include/dart_api.h"
 #include "third_party/tonic/converter/dart_converter.h"
 #include "third_party/tonic/dart_microtask_queue.h"
 #include "third_party/tonic/logging/dart_error.h"
-#include "third_party/dart/runtime/bin/io_natives.h"
-#include "third_party/dart/runtime/include/dart_api.h"
 #include "topaz/lib/deprecated_loop/message_loop.h"
 
 using tonic::ToDart;
@@ -91,13 +91,11 @@
 
 void InitBuiltinLibrariesForIsolate(
     const std::string& script_uri, fdio_ns_t* namespc, int stdoutfd,
-    int stderrfd, std::unique_ptr<component::StartupContext> context,
+    int stderrfd, fidl::InterfaceHandle<fuchsia::sys::Environment> environment,
     fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> outgoing_services,
     bool service_isolate) {
   // dart:fuchsia --------------------------------------------------------------
   if (!service_isolate) {
-    fidl::InterfaceHandle<fuchsia::sys::Environment> environment;
-    context->ConnectToEnvironmentService(environment.NewRequest());
     fuchsia::dart::Initialize(std::move(environment),
                               std::move(outgoing_services));
   }
diff --git a/runtime/dart_runner/builtin_libraries.h b/runtime/dart_runner/builtin_libraries.h
index 1dceff0..7f6e9b5 100644
--- a/runtime/dart_runner/builtin_libraries.h
+++ b/runtime/dart_runner/builtin_libraries.h
@@ -17,7 +17,7 @@
 
 void InitBuiltinLibrariesForIsolate(
     const std::string& script_uri, fdio_ns_t* namespc, int stdoutfd,
-    int stderrfd, std::unique_ptr<component::StartupContext> context,
+    int stderrfd, fidl::InterfaceHandle<fuchsia::sys::Environment> environment,
     fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> outgoing_services,
     bool service_isolate);
 
diff --git a/runtime/dart_runner/dart_component_controller.cc b/runtime/dart_runner/dart_component_controller.cc
index 7b3559d..11a5d3d 100644
--- a/runtime/dart_runner/dart_component_controller.cc
+++ b/runtime/dart_runner/dart_component_controller.cc
@@ -28,7 +28,9 @@
 #include "third_party/tonic/dart_state.h"
 #include "third_party/tonic/logging/dart_error.h"
 #include "topaz/lib/deprecated_loop/message_loop.h"
-#include "topaz/runtime/dart_runner/builtin_libraries.h"
+#include "topaz/runtime/dart/utils/handle_exception.h"
+
+#include "builtin_libraries.h"
 
 using tonic::ToDart;
 
@@ -342,9 +344,12 @@
   stdoutfd_ = SetupFileDescriptor(std::move(startup_info_.launch_info.out));
   stderrfd_ = SetupFileDescriptor(std::move(startup_info_.launch_info.err));
 
+  context_ = component::StartupContext::CreateFrom(std::move(startup_info_));
+  fidl::InterfaceHandle<fuchsia::sys::Environment> environment;
+  context_->ConnectToEnvironmentService(environment.NewRequest());
+
   InitBuiltinLibrariesForIsolate(
-      url_, namespace_, stdoutfd_, stderrfd_,
-      component::StartupContext::CreateFrom(std::move(startup_info_)),
+      url_, namespace_, stdoutfd_, stderrfd_, std::move(environment),
       std::move(outgoing_services), false /* service_isolate */);
   namespace_ = nullptr;
 
@@ -386,6 +391,8 @@
       // The program hasn't set a return code meaning this exit is unexpected.
       FXL_LOG(ERROR) << Dart_GetError(main_result);
       return_code_ = tonic::GetErrorExitCode(main_result);
+
+      fuchsia::dart::HandleIfException(context_.get(), url_, main_result);
     }
     Dart_ExitScope();
     return false;
@@ -434,6 +441,8 @@
     return;
   }
 
+  fuchsia::dart::HandleIfException(context_.get(), url_, result);
+
   // Otherwise, see if there was any other error.
   return_code_ = tonic::GetErrorExitCode(result);
   if (return_code_ != 0) {
diff --git a/runtime/dart_runner/dart_component_controller.h b/runtime/dart_runner/dart_component_controller.h
index 961fce2..551ee47 100644
--- a/runtime/dart_runner/dart_component_controller.h
+++ b/runtime/dart_runner/dart_component_controller.h
@@ -10,6 +10,7 @@
 #include <lib/zx/timer.h>
 
 #include <fuchsia/sys/cpp/fidl.h>
+#include "lib/component/cpp/startup_context.h"
 #include "lib/fidl/cpp/binding.h"
 #include "lib/fsl/vmo/sized_vmo.h"
 #include "lib/fxl/macros.h"
@@ -60,6 +61,7 @@
   std::string data_path_;
   component::ServiceProviderBridge service_provider_bridge_;
   fidl::Binding<fuchsia::sys::ComponentController> binding_;
+  std::unique_ptr<component::StartupContext> context_;
 
   fdio_ns_t* namespace_ = nullptr;
   int stdoutfd_ = -1;