Rename /tests to /ckm to align with tizen branch
[platform/core/test/security-tests.git] / src / framework / src / gdbbacktrace.cpp
1 /*
2  * Copyright (c) 2014-2015 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  * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
21  * @version     1.0
22  * @brief       API providing backtrace
23  */
24
25 #include <cerrno>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cstring>
29 #include <fcntl.h>
30 #include <iomanip>
31 #include <iostream>
32 #include <regex>
33 #include <sstream>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38
39 #include <dpl/colors.h>
40
41 #include <dpl/gdbbacktrace.h>
42
43 namespace DPL {
44 namespace {
45
46 const std::string FRAME_PATTERN_DPL("in DPL::Test");
47 const std::string FRAME_GDBBACKTRACE("in DPL::gdbbacktrace");
48 const std::string DEV_NULL("/dev/null");
49 const std::string TMP_FILE_PREFIX("/tmp/security-tests_gdbbacktrace-");
50
51 void printColor(const char *err, int errnoNumber = 0)
52 {
53     std::cerr << Colors::Text::RED_BEGIN << "gdbbacktrace() failed: " << err
54               << ((errnoNumber != 0) ? (std::string(". ") + strerror(errnoNumber)) : "")
55               << Colors::Text::RED_END << std::endl;
56 }
57
58 bool backtraceParseLine(const std::string &line, std::ostream &result, size_t lineNumber)
59 {
60     if (line.find(FRAME_PATTERN_DPL, 0) != std::string::npos)
61         return false;
62
63     std::smatch m;
64     std::regex expr("^#\\d+\\s+0x[0-9a-fA-F]+\\s+(.+)");
65     if (!std::regex_search (line, m, expr))
66         return false;
67
68     result << "#" << std::left << std::setw(2) << lineNumber << " " << m[m.size()-1] << std::endl;
69     return true;
70 }
71
72 std::string backtraceRead(const std::string &filename)
73 {
74     errno = 0;
75     FILE *bt_fd = fopen(filename.c_str(), "r");
76     if (bt_fd == nullptr) {
77         printColor("fopen() failed", errno);
78         return "";
79     }
80
81     std::ostringstream result;
82     result << std::endl;
83
84     char * line = nullptr;
85     size_t len = 0;
86
87     while (true) {
88         if (-1 == getline(&line, &len, bt_fd)) {
89             printColor("No backtrace information", errno);
90             free(line);
91             fclose(bt_fd);
92             return "";
93         }
94         if (std::string(line).find(FRAME_GDBBACKTRACE, 0) != std::string::npos)
95             break;
96     }
97
98     size_t lineNumber = 1;
99     while (-1 != getline(&line, &len, bt_fd)) {
100         if (backtraceParseLine(line, result, lineNumber))
101             ++lineNumber;
102     }
103
104     free(line);
105     fclose(bt_fd);
106
107     return result.str();
108 }
109
110 void runChild()
111 {
112     try {
113         std::string filename = TMP_FILE_PREFIX + std::to_string(getpid());
114         int devnullWrite = TEMP_FAILURE_RETRY(open(DEV_NULL.c_str(), O_WRONLY));
115         if (-1 == devnullWrite)
116             exit(EXIT_FAILURE);
117         int writeFd = TEMP_FAILURE_RETRY(creat(filename.c_str(), S_IRUSR | S_IWUSR));
118         if (-1 == writeFd)
119             exit(EXIT_FAILURE);
120         int devnullRead = TEMP_FAILURE_RETRY(open(DEV_NULL.c_str(), O_RDONLY));
121         if (-1 == devnullRead)
122             exit(EXIT_FAILURE);
123         if (-1 == TEMP_FAILURE_RETRY(dup2(devnullWrite, STDERR_FILENO)))
124             exit(EXIT_FAILURE);
125         if (-1 == TEMP_FAILURE_RETRY(dup2(writeFd, STDOUT_FILENO)))
126             exit(EXIT_FAILURE);
127         if (-1 == TEMP_FAILURE_RETRY(dup2(devnullRead, STDIN_FILENO)))
128             exit(EXIT_FAILURE);
129         std::string ppid = std::to_string(getppid());
130         execl("/usr/bin/gdb", "gdb", "--batch", "-n", "-ex", "bt", "--pid", ppid.c_str(),
131               (char*)nullptr);
132         // gdb failed to start...
133     } catch (...) {
134     }
135     exit(EXIT_FAILURE);
136 }
137
138 } // namespace
139
140 std::string gdbbacktrace(void)
141 {
142     std::string ret;
143
144     pid_t childPid = fork();
145     switch (childPid) {
146         case -1:
147             printColor("fork() failed", errno);
148             return ret;
149         case 0:
150             runChild();
151         default:
152             break;
153     }
154
155     std::string filename = TMP_FILE_PREFIX + std::to_string(childPid);
156
157     int status;
158     pid_t pid = TEMP_FAILURE_RETRY(waitpid(childPid, &status, 0));
159     if (-1 == pid)
160         printColor("waitpid() failed", errno);
161     else if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
162         printColor("Error: no gdb or failed to start gdb...");
163     else
164         ret = backtraceRead(filename);
165
166     if (-1 == unlink(filename.c_str()) && errno != ENOENT)
167         printColor("unlink() failed", errno);
168     return ret;
169 }
170
171 } // namespace DPL