Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e7f0dec authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Dump stacktraces for rootcanal, and also dump them during RPC errors"

parents b53a3af0 fdd9e9d9
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ from acts.context import get_current_context
from acts.base_test import BaseTestClass

from cert.os_utils import get_gd_root
from cert.os_utils import read_crash_snippet_and_log_tail
from cert.os_utils import is_subprocess_alive
from cert.os_utils import make_ports_available
from facade import rootservice_pb2 as facade_rootservice
@@ -54,9 +55,9 @@ class GdBaseTestClass(BaseTestClass):
                "Root canal does not exist at %s" % rootcanal)

            # Get root canal log
            rootcanal_logpath = os.path.join(self.log_path_base,
            self.rootcanal_logpath = os.path.join(self.log_path_base,
                                                  'rootcanal_logs.txt')
            self.rootcanal_logs = open(rootcanal_logpath, 'w')
            self.rootcanal_logs = open(self.rootcanal_logpath, 'w')

            # Make sure ports are available
            rootcanal_config = self.controller_configs['rootcanal']
@@ -171,14 +172,24 @@ class GdBaseTestClass(BaseTestClass):
        """
        dut_crash, dut_log_tail = self.dut.get_crash_snippet_and_log_tail()
        cert_crash, cert_log_tail = self.cert.get_crash_snippet_and_log_tail()
        rootcanal_crash = None
        rootcanal_log_tail = None
        if self.rootcanal_running and not is_subprocess_alive(
                self.rootcanal_process):
            rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(
                self.rootcanal_logpath)

        crash_detail = ""
        if dut_crash or cert_crash:
        if dut_crash or cert_crash or rootcanal_crash:
            if rootcanal_crash:
                crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash
            if dut_crash:
                crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash
            if cert_crash:
                crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash
        else:
            if rootcanal_log_tail:
                crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail
            if dut_log_tail:
                crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail
            if cert_log_tail:
+2 −38
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
#   limitations under the License.

from abc import ABC
from collections import deque
import inspect
import logging
import os
@@ -38,6 +37,7 @@ from acts.controllers.adb import AdbError
from google.protobuf import empty_pb2 as empty_proto

from cert.os_utils import get_gd_root
from cert.os_utils import read_crash_snippet_and_log_tail
from cert.os_utils import is_subprocess_alive
from cert.os_utils import make_ports_available
from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
@@ -249,47 +249,11 @@ class GdDeviceBase(ABC):
        self.security = security_facade_pb2_grpc.SecurityModuleFacadeStub(
            self.grpc_channel)

    # e.g. 2020-05-06 16:02:04.216 bt - packages/modules/Bluetooth/system/gd/facade/facade_main.cc:79 - crash_callback: #03 pc 0000000000013520  /lib/x86_64-linux-gnu/libpthread-2.29.so
    HOST_CRASH_LINE_REGEX = re.compile(r"^.* - crash_callback: (?P<line>.*)$")
    HOST_CRASH_HEADER = "Process crashed, signal: Aborted"

    def get_crash_snippet_and_log_tail(self):
        """
        Get crash snippet if regex matched or last 20 lines of log
        :return: crash_snippet, log_tail_20
                1) crash snippet without timestamp in one string;
                2) last 20 lines of log in one string;
        """
        if is_subprocess_alive(self.backing_process):
            return None, None

        gd_root_prefix = get_gd_root() + "/"
        abort_line = None
        last_20_lines = deque(maxlen=20)
        crash_log_lines = []

        with open(self.backing_process_log_path) as f:
            for _, line in enumerate(f):
                m = self.HOST_CRASH_LINE_REGEX.match(line)
                if m:
                    crash_line = m.group("line").replace(gd_root_prefix, "")
                    if self.HOST_CRASH_HEADER in crash_line \
                            and len(last_20_lines) > 0:
                        abort_line = last_20_lines[-1]
                    crash_log_lines.append(crash_line)
                last_20_lines.append(line)

        log_tail_20 = "".join(last_20_lines)

        if len(crash_log_lines) == 0:
            return None, log_tail_20

        crash_snippet = ""
        if abort_line is not None:
            crash_snippet += "abort log line:\n\n%s\n" % abort_line
        crash_snippet += "\n".join(crash_log_lines)

        return crash_snippet, log_tail_20
        return read_crash_snippet_and_log_tail(self.backing_process_log_path)

    def teardown(self):
        """Tear down this device and clean up any resources.
+42 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ from pathlib import Path
import psutil
import subprocess
from typing import Container
from collections import deque


def is_subprocess_alive(process, timeout_seconds=1):
@@ -82,3 +83,44 @@ def make_ports_available(ports: Container[int], timeout_seconds=10):
                          (timeout_seconds, conn.pid))
            continue
    return success


# e.g. 2020-05-06 16:02:04.216 bt - packages/modules/Bluetooth/system/gd/facade/facade_main.cc:79 - crash_callback: #03 pc 0000000000013520  /lib/x86_64-linux-gnu/libpthread-2.29.so
HOST_CRASH_LINE_REGEX = re.compile(r"^.* - crash_callback: (?P<line>.*)$")
HOST_ABORT_HEADER = "Process crashed, signal: Aborted"


def read_crash_snippet_and_log_tail(logpath):
    """
    Get crash snippet if regex matched or last 20 lines of log
    :return: crash_snippet, log_tail_20
            1) crash snippet without timestamp in one string;
            2) last 20 lines of log in one string;
    """
    gd_root_prefix = get_gd_root() + "/"
    abort_line = None
    last_20_lines = deque(maxlen=20)
    crash_log_lines = []

    with open(logpath) as f:
        for _, line in enumerate(f):
            m = HOST_CRASH_LINE_REGEX.match(line)
            if m:
                crash_line = m.group("line").replace(gd_root_prefix, "")
                if HOST_ABORT_HEADER in crash_line \
                        and len(last_20_lines) > 0:
                    abort_line = last_20_lines[-1]
                crash_log_lines.append(crash_line)
            last_20_lines.append(line)

    log_tail_20 = "".join(last_20_lines)

    if len(crash_log_lines) == 0:
        return None, log_tail_20

    crash_snippet = ""
    if abort_line is not None:
        crash_snippet += "abort log line:\n\n%s\n" % abort_line
    crash_snippet += "\n".join(crash_log_lines)

    return crash_snippet, log_tail_20
+2 −0
Original line number Diff line number Diff line
@@ -146,12 +146,14 @@ cc_binary_host {
    ],
    shared_libs: [
        "liblog",
        "libbacktrace",
    ],
    static_libs: [
        "libbt-rootcanal-types",
        "libprotobuf-cpp-lite",
        "libscriptedbeaconpayload-protos-lite",
        "libbt-rootcanal",
        "breakpad_client",
    ],
    sanitize: {
        address: true,
+43 −0
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@

#include <future>

#include <client/linux/handler/exception_handler.h>

#include <backtrace/Backtrace.h>
#include <backtrace/backtrace_constants.h>

#include "os/log.h"

using ::android::bluetooth::root_canal::TestEnvironment;
@@ -26,7 +31,45 @@ constexpr uint16_t kTestPort = 6401;
constexpr uint16_t kHciServerPort = 6402;
constexpr uint16_t kLinkServerPort = 6403;

bool crash_callback(const void* crash_context, size_t crash_context_size,
                    __attribute__((unused)) void* context) {
  pid_t tid = BACKTRACE_CURRENT_THREAD;
  if (crash_context_size >=
      sizeof(google_breakpad::ExceptionHandler::CrashContext)) {
    auto* ctx =
        static_cast<const google_breakpad::ExceptionHandler::CrashContext*>(
            crash_context);
    tid = ctx->tid;
    int signal_number = ctx->siginfo.si_signo;
    LOG_ERROR("Process crashed, signal: %s[%d], tid: %d",
              strsignal(signal_number), signal_number, ctx->tid);
  } else {
    LOG_ERROR("Process crashed, signal: unknown, tid: unknown");
  }
  std::unique_ptr<Backtrace> backtrace(
      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
  if (backtrace == nullptr) {
    LOG_ERROR("Failed to create backtrace object");
    return false;
  }
  if (!backtrace->Unwind(0)) {
    LOG_ERROR("backtrace->Unwind failed");
    return false;
  }
  LOG_ERROR("Backtrace:");
  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
    LOG_ERROR("%s", backtrace->FormatFrameData(i).c_str());
  }
  return true;
}

int main(int argc, char** argv) {
  google_breakpad::MinidumpDescriptor descriptor(
      google_breakpad::MinidumpDescriptor::kMicrodumpOnConsole);
  google_breakpad::ExceptionHandler eh(descriptor, nullptr, nullptr, nullptr,
                                       true, -1);
  eh.set_crash_handler(crash_callback);

  LOG_INFO("main");
  uint16_t test_port = kTestPort;
  uint16_t hci_server_port = kHciServerPort;