From c313a71705cf7c7ede25a5cd6b31462f8bcddc1e Mon Sep 17 00:00:00 2001 From: Pawel Broda Date: Tue, 10 Dec 2013 16:24:09 +0100 Subject: [PATCH] DPL enhancement - line printing in case of error [Issue#] SSDWSSP-710 [Feature] Provides backtrace (i.e. source code file name + line number) in case of error. [Cause] Info amount in case of test failure was not sufficient to determine execution path. There was a need of passing __LINE__ or similar preprocessor macros as arguments. [Solution] Backtrace printing based on the gdb output (gdb is triggered after particular test fails). [Verification] Compile & install on the target. Run without gdb installed too. Run example tests which fails and examine the output. Change-Id: I749b8efa9ce457df41baebbaefd6aec869f772a7 --- tests/common/CMakeLists.txt | 1 + tests/common/gdbbacktrace.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++ tests/common/gdbbacktrace.h | 31 ++++++++++ tests/common/tests_common.cpp | 1 - tests/common/tests_common.h | 5 ++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 tests/common/gdbbacktrace.cpp create mode 100644 tests/common/gdbbacktrace.h diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index 59e1d93..040dcfb 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -19,6 +19,7 @@ SET(COMMON_TARGET_TEST_SOURCES ${PROJECT_SOURCE_DIR}/tests/common/smack_access.cpp ${PROJECT_SOURCE_DIR}/tests/common/summary_collector.cpp ${PROJECT_SOURCE_DIR}/tests/common/dbus_access.cpp + ${PROJECT_SOURCE_DIR}/tests/common/gdbbacktrace.cpp ) #header directories diff --git a/tests/common/gdbbacktrace.cpp b/tests/common/gdbbacktrace.cpp new file mode 100644 index 0000000..8356b46 --- /dev/null +++ b/tests/common/gdbbacktrace.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file gdbbacktrace.cpp + * @author Pawel Broda (p.broda@partner.samsung.com) + * @version 1.0 + * @brief API providing backtrace + */ + +#include +#include +#include +#include +#include + +#include "gdbbacktrace.h" + +namespace { +const size_t MAX_BACKTRACE_LINE_LENGTH = 1024; +const std::string COLOUR_CODE_RED("\033[31m"); +const std::string COLOUR_CODE_RESET("\033[0m"); + +const std::string FRAME_PATTERN_WAITPID("#0 "); +const std::string FRAME_PATTERN_BACKTRACE("#1 "); +const std::string FRAME_PATTERN_DPL("in DPL::Test"); + +void print_colour(const std::string& err) +{ + std::cerr << COLOUR_CODE_RED << err << COLOUR_CODE_RESET << std::endl; +} + +bool backtrace_parse_line(const std::string& line, std::ostream& result, size_t line_number) +{ + if (line.empty() || line[0] != '#') + return false; + + // backtrace info - omit waitpid(), backtrace() and DPL frames + if (line.find(FRAME_PATTERN_WAITPID) == 0) + return false; + + if (line.find(FRAME_PATTERN_BACKTRACE) == 0) + return false; + + if (line.find(FRAME_PATTERN_DPL) != std::string::npos) + return false; + + // example std::string line content: + // "#5 0x000000000040198d in main () at ../src/backtrace.cpp:105" + // should result in (i.e. would be written into char line_formatted): + // "main () at ../src/backtrace.cpp:105" + + char line_formatted[MAX_BACKTRACE_LINE_LENGTH]; + line_formatted[0] = '\0'; + sscanf(line.c_str(), "%*s %*s %[^\n]", line_formatted); + if (line_formatted[0] != '\0') { + result << "#" << std::left << std::setw(2) << line_number << " " << line_formatted << + std::endl; + return true; + } + + return false; +} + +std::string backtrace_read(int fd) +{ + FILE *bt_fd = fdopen(fd, "r"); + char read_buffer[MAX_BACKTRACE_LINE_LENGTH]; + std::ostringstream result; + + size_t line_number = 1; + while (fgets(read_buffer, sizeof(read_buffer) - 1, bt_fd) != NULL) { + if (backtrace_parse_line(read_buffer, result, line_number)) + ++line_number; + } + + fclose(bt_fd); + return result.str(); +} +} // anonymous backtrace namespace end + +std::string gdbbacktrace(void) +{ + std::string pid_buf = std::to_string(getpid()); + int pipe_fd[2]; + pipe(pipe_fd); + + int child_pid = fork(); + if (child_pid == -1) + { + print_colour("fork needed to run gdb in batch mode failed..."); + return ""; + } + + if (child_pid == 0) { + int devnull = open("/dev/null", O_WRONLY); + if (dup2(devnull, 2) == -1) + exit(2); + if (dup2(pipe_fd[1], 1) == -1) + exit(2); + execlp("/usr/bin/gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", "--pid", + pid_buf.c_str(), NULL); + // gdb failed to start... + exit(1); + } + + int status; + int waitpid_res = waitpid(child_pid, &status, 0); + + close(pipe_fd[1]); + + std::string result; + if (waitpid_res == -1) { + print_colour("Backtrace not available (waitpid failed)"); + } else if (status == 2) { + print_colour("Error: file descriptor duplication failed... failed to start gdb..."); + } else if (status != 0) { + print_colour("Error: no gdb or failed to start gdb..."); + } else { + result.append(backtrace_read(pipe_fd[0])); + } + + close(pipe_fd[0]); + + return result; +} diff --git a/tests/common/gdbbacktrace.h b/tests/common/gdbbacktrace.h new file mode 100644 index 0000000..8a3028b --- /dev/null +++ b/tests/common/gdbbacktrace.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file gdbbacktrace.h + * @author Pawel Broda (p.broda@partner.samsung.com) + * @version 1.0 + * @brief API providing backtrace + */ + +#ifndef _GDBBACKTRACE_H_ +#define _GDBBACKTRACE_H_ + +#include + +std::string gdbbacktrace(void); + +#endif diff --git a/tests/common/tests_common.cpp b/tests/common/tests_common.cpp index f8d2d67..4665c13 100644 --- a/tests/common/tests_common.cpp +++ b/tests/common/tests_common.cpp @@ -80,4 +80,3 @@ void setLabelForSelf(const int line, const char *label) int ret = smack_set_label_for_self(label); RUNNER_ASSERT_MSG(ret == 0, "Error in smack_set_label_for_self(): " << ret << ", line: " << line); } - diff --git a/tests/common/tests_common.h b/tests/common/tests_common.h index 1722c0b..57880c6 100644 --- a/tests/common/tests_common.h +++ b/tests/common/tests_common.h @@ -31,6 +31,8 @@ #include #include +#include "gdbbacktrace.h" + const uid_t APP_UID = 5000; const gid_t APP_GID = 5000; const uid_t DB_ALARM_UID = 6001; @@ -127,6 +129,9 @@ int drop_root_privileges(void); } \ void Proc##Multi() +#define RUNNER_ASSERT_MSG_BT(test, msg) RUNNER_ASSERT_MSG(test,\ + msg + std::string("\n") + gdbbacktrace()) +#define RUNNER_ASSERT_BT(test) RUNNER_ASSERT_MSG_BT(test, "") void closeFdPtr(int *fd); void setLabelForSelf(const int line, const char *label); -- 2.7.4