Loading system/gd/cert/gd_base_test.py +15 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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'] Loading Loading @@ -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: Loading system/gd/cert/gd_device.py +2 −38 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ # limitations under the License. from abc import ABC from collections import deque import inspect import logging import os Loading @@ -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 Loading Loading @@ -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. Loading system/gd/cert/os_utils.py +42 −0 Original line number Diff line number Diff line Loading @@ -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): Loading Loading @@ -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 system/vendor_libs/test_vendor_lib/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -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, Loading system/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc +43 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading
system/gd/cert/gd_base_test.py +15 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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'] Loading Loading @@ -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: Loading
system/gd/cert/gd_device.py +2 −38 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ # limitations under the License. from abc import ABC from collections import deque import inspect import logging import os Loading @@ -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 Loading Loading @@ -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. Loading
system/gd/cert/os_utils.py +42 −0 Original line number Diff line number Diff line Loading @@ -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): Loading Loading @@ -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
system/vendor_libs/test_vendor_lib/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
system/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc +43 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading