Send bactrace to stderr and capture it in test runner

Servo's panic hook writes backtraces to stdout. This
patch changes it so they are written to stderr.

The crash test executor for servo in WPT grouping formatter
was also not capturing the output correctly for crashtests
as the log events were being aggregated based on thread name
which doesn't seem to match correctly in case of crashtests.
This patch also fixes the log grouping logic to be based on
test name.

Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
diff --git a/tools/wptrunner/wptrunner/executors/executorservo.py b/tools/wptrunner/wptrunner/executors/executorservo.py
index a8ec386..3250c74 100644
--- a/tools/wptrunner/wptrunner/executors/executorservo.py
+++ b/tools/wptrunner/wptrunner/executors/executorservo.py
@@ -83,6 +83,7 @@
         ProcessTestExecutor.teardown(self)
 
     def do_test(self, test):
+        self.test = test
         self.result_data = None
         self.result_flag = threading.Event()
 
@@ -156,7 +157,7 @@
             else:
                 self.logger.process_output(self.proc.pid,
                                            line,
-                                           " ".join(self.command))
+                                           " ".join(self.command), self.test.url)
 
     def on_finish(self):
         self.result_flag.set()
@@ -270,6 +271,7 @@
                 return True, [base64.b64encode(data).decode()]
 
     def do_test(self, test):
+        self.test = test
         result = self.implementation.run_test(test)
 
         return self.convert_result(test, result)
@@ -281,7 +283,7 @@
         else:
             self.logger.process_output(self.proc.pid,
                                        line,
-                                       " ".join(self.command))
+                                   " ".join(self.command), self.test.url)
 
 
 class ServoTimedRunner(TimedRunner):
@@ -342,21 +344,22 @@
         env["HOST_FILE"] = self.hosts_path
         env["RUST_BACKTRACE"] = "1"
 
-        command = build_servo_command(self.test,
-                                      self.test_url,
-                                      self.browser,
-                                      self.binary,
-                                      False,
-                                      self.debug_info,
-                                      extra_args=["-x"])
+        self.command = build_servo_command(self.test,
+                                           self.test_url,
+                                           self.browser,
+                                           self.binary,
+                                           False,
+                                           self.debug_info,
+                                           extra_args=["-x"])
 
         if not self.interactive:
-            self.proc = ProcessHandler(command,
+            self.proc = ProcessHandler(self.command,
                                        env=env,
+                                       processOutputLine=[self.on_output],
                                        storeOutput=False)
             self.proc.run()
         else:
-            self.proc = subprocess.Popen(command, env=env)
+            self.proc = subprocess.Popen(self.command, env=env)
 
         self.proc.wait()
 
@@ -364,3 +367,9 @@
             return {"status": "PASS", "message": None}
 
         return {"status": "CRASH", "message": None}
+
+    def on_output(self, line):
+        line = line.decode("utf8", "replace")
+        self.logger.process_output(self.proc.pid,
+                                   line,
+                                   " ".join(self.command), self.test.url)