DPL enhancement - line printing in case of error
[platform/core/test/security-tests.git] / tests / common / gdbbacktrace.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 /*
18  * @file        gdbbacktrace.cpp
19  * @author      Pawel Broda (p.broda@partner.samsung.com)
20  * @version     1.0
21  * @brief       API providing backtrace
22  */
23
24 #include <fcntl.h>
25 #include <iomanip>
26 #include <iostream>
27 #include <sstream>
28 #include <sys/wait.h>
29
30 #include "gdbbacktrace.h"
31
32 namespace {
33 const size_t MAX_BACKTRACE_LINE_LENGTH = 1024;
34 const std::string COLOUR_CODE_RED("\033[31m");
35 const std::string COLOUR_CODE_RESET("\033[0m");
36
37 const std::string FRAME_PATTERN_WAITPID("#0  ");
38 const std::string FRAME_PATTERN_BACKTRACE("#1  ");
39 const std::string FRAME_PATTERN_DPL("in DPL::Test");
40
41 void print_colour(const std::string& err)
42 {
43     std::cerr << COLOUR_CODE_RED << err << COLOUR_CODE_RESET << std::endl;
44 }
45
46 bool backtrace_parse_line(const std::string& line, std::ostream& result, size_t line_number)
47 {
48     if (line.empty() || line[0] != '#')
49         return false;
50
51     // backtrace info - omit waitpid(), backtrace() and DPL frames
52     if (line.find(FRAME_PATTERN_WAITPID) == 0)
53         return false;
54
55     if (line.find(FRAME_PATTERN_BACKTRACE) == 0)
56         return false;
57
58     if (line.find(FRAME_PATTERN_DPL) != std::string::npos)
59         return false;
60
61     // example std::string line content:
62     // "#5  0x000000000040198d in main () at ../src/backtrace.cpp:105"
63     // should result in (i.e. would be written into char line_formatted):
64     // "main () at ../src/backtrace.cpp:105"
65
66     char line_formatted[MAX_BACKTRACE_LINE_LENGTH];
67     line_formatted[0] = '\0';
68     sscanf(line.c_str(), "%*s %*s %[^\n]", line_formatted);
69     if (line_formatted[0] != '\0') {
70         result << "#" << std::left << std::setw(2) << line_number << " " << line_formatted <<
71                   std::endl;
72         return true;
73     }
74
75     return false;
76 }
77
78 std::string backtrace_read(int fd)
79 {
80     FILE *bt_fd = fdopen(fd, "r");
81     char read_buffer[MAX_BACKTRACE_LINE_LENGTH];
82     std::ostringstream result;
83
84     size_t line_number = 1;
85     while (fgets(read_buffer, sizeof(read_buffer) - 1, bt_fd) != NULL) {
86         if (backtrace_parse_line(read_buffer, result, line_number))
87             ++line_number;
88     }
89
90     fclose(bt_fd);
91     return result.str();
92 }
93 } // anonymous backtrace namespace end
94
95 std::string gdbbacktrace(void)
96 {
97     std::string pid_buf = std::to_string(getpid());
98     int pipe_fd[2];
99     pipe(pipe_fd);
100
101     int child_pid = fork();
102     if (child_pid == -1)
103     {
104         print_colour("fork needed to run gdb in batch mode failed...");
105         return "";
106     }
107
108     if (child_pid == 0) {
109         int devnull = open("/dev/null", O_WRONLY);
110         if (dup2(devnull, 2) == -1)
111             exit(2);
112         if (dup2(pipe_fd[1], 1) == -1)
113             exit(2);
114         execlp("/usr/bin/gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", "--pid",
115                pid_buf.c_str(), NULL);
116         // gdb failed to start...
117         exit(1);
118     }
119
120     int status;
121     int waitpid_res = waitpid(child_pid, &status, 0);
122
123     close(pipe_fd[1]);
124
125     std::string result;
126     if (waitpid_res == -1) {
127         print_colour("Backtrace not available (waitpid failed)");
128     } else if (status == 2) {
129         print_colour("Error: file descriptor duplication failed... failed to start gdb...");
130     } else if (status != 0) {
131         print_colour("Error: no gdb or failed to start gdb...");
132     } else {
133         result.append(backtrace_read(pipe_fd[0]));
134     }
135
136     close(pipe_fd[0]);
137
138     return result;
139 }