2 * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @file gdbbacktrace.cpp
19 * @author Pawel Broda (p.broda@partner.samsung.com)
20 * @author Marcin Niesluchowski <m.niesluchow@samsung.com>
22 * @brief API providing backtrace
35 #include <sys/types.h>
39 #include <dpl/colors.h>
41 #include <dpl/gdbbacktrace.h>
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-");
51 void printColor(const char *err, int errnoNumber = 0)
53 std::cerr << Colors::Text::RED_BEGIN << "gdbbacktrace() failed: " << err
54 << ((errnoNumber != 0) ? (std::string(". ") + strerror(errnoNumber)) : "")
55 << Colors::Text::RED_END << std::endl;
58 bool backtraceParseLine(const std::string &line, std::ostream &result, size_t lineNumber)
60 if (line.find(FRAME_PATTERN_DPL, 0) != std::string::npos)
64 std::regex expr("^#\\d+\\s+0x[0-9a-fA-F]+\\s+(.+)");
65 if (!std::regex_search (line, m, expr))
68 result << "#" << std::left << std::setw(2) << lineNumber << " " << m[m.size()-1] << std::endl;
72 std::string backtraceRead(const std::string &filename)
75 FILE *bt_fd = fopen(filename.c_str(), "r");
76 if (bt_fd == nullptr) {
77 printColor("fopen() failed", errno);
81 std::ostringstream result;
84 char * line = nullptr;
88 if (-1 == getline(&line, &len, bt_fd)) {
89 printColor("No backtrace information", errno);
94 if (std::string(line).find(FRAME_GDBBACKTRACE, 0) != std::string::npos)
98 size_t lineNumber = 1;
99 while (-1 != getline(&line, &len, bt_fd)) {
100 if (backtraceParseLine(line, result, lineNumber))
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)
117 int writeFd = TEMP_FAILURE_RETRY(creat(filename.c_str(), S_IRUSR | S_IWUSR));
120 int devnullRead = TEMP_FAILURE_RETRY(open(DEV_NULL.c_str(), O_RDONLY));
121 if (-1 == devnullRead)
123 if (-1 == TEMP_FAILURE_RETRY(dup2(devnullWrite, STDERR_FILENO)))
125 if (-1 == TEMP_FAILURE_RETRY(dup2(writeFd, STDOUT_FILENO)))
127 if (-1 == TEMP_FAILURE_RETRY(dup2(devnullRead, STDIN_FILENO)))
129 std::string ppid = std::to_string(getppid());
130 execl("/usr/bin/gdb", "gdb", "--batch", "-n", "-ex", "bt", "--pid", ppid.c_str(),
132 // gdb failed to start...
140 std::string gdbbacktrace(void)
144 pid_t childPid = fork();
147 printColor("fork() failed", errno);
155 std::string filename = TMP_FILE_PREFIX + std::to_string(childPid);
158 pid_t pid = TEMP_FAILURE_RETRY(waitpid(childPid, &status, 0));
160 printColor("waitpid() failed", errno);
161 else if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
162 printColor("Error: no gdb or failed to start gdb...");
164 ret = backtraceRead(filename);
166 if (-1 == unlink(filename.c_str()) && errno != ENOENT)
167 printColor("unlink() failed", errno);