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