DPL enhancement - line printing in case of error
authorPawel Broda <p.broda@partner.samsung.com>
Tue, 10 Dec 2013 15:24:09 +0000 (16:24 +0100)
committerMarcin Niesluchowski <m.niesluchow@samsung.com>
Thu, 23 Jan 2014 14:21:36 +0000 (15:21 +0100)
[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
tests/common/gdbbacktrace.cpp [new file with mode: 0644]
tests/common/gdbbacktrace.h [new file with mode: 0644]
tests/common/tests_common.cpp
tests/common/tests_common.h

index 59e1d93..040dcfb 100644 (file)
@@ -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 (file)
index 0000000..8356b46
--- /dev/null
@@ -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 <fcntl.h>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <sys/wait.h>
+
+#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 (file)
index 0000000..8a3028b
--- /dev/null
@@ -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 <string>
+
+std::string gdbbacktrace(void);
+
+#endif
index f8d2d67..4665c13 100644 (file)
@@ -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);
 }
-
index 1722c0b..57880c6 100644 (file)
@@ -31,6 +31,8 @@
 #include <privilege-control.h>
 #include <sys/smack.h>
 
+#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);