2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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 #ifndef LIVEDUMPER_HPP__
19 #define LIVEDUMPER_HPP__
22 #include "helpers.hpp"
23 #include "prochandle.hpp"
31 #include <sys/ptrace.h>
45 std::vector<pid_t> m_tpids;
46 std::map<pid_t,user_regs_struct> m_registers;
48 std::ofstream m_core_file;
50 std::pair <void *, size_t> map_buff;
53 explicit LiveDumper(const pid_t pid) : m_pid(pid), map_buff(MAP_FAILED, 0) {}
56 if (map_buff.first != MAP_FAILED)
57 munmap(map_buff.first, map_buff.second);
63 std::string task_dir = "/proc/" + std::to_string(m_pid) + "/task";
65 DIR *dir = opendir(task_dir.c_str());
67 throw std::system_error(errno, std::system_category(), "failed to open directory: " + task_dir);
70 while ((ent = readdir(dir)) != nullptr) {
71 if (ent->d_type == DT_DIR &&
72 strncmp(ent->d_name, ".", 1) != 0)
73 m_tpids.push_back(std::strtol(ent->d_name, 0, 10));
78 void AttachTo(const pid_t pid) const {
79 if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) == -1)
80 throw std::system_error(errno, std::system_category(), "failed to attach to: " + std::to_string(pid));
83 void setPrstatusFd(const int prstatus_fd) {
84 this->prstatus_fd = prstatus_fd;
90 waitpid(m_pid, nullptr, 0);
94 for (const auto &pid : m_tpids) {
95 if (pid == m_pid) continue;
100 void Detach() const {
101 for (const auto &pid : m_tpids) {
102 if (ptrace(PTRACE_DETACH, pid, nullptr, nullptr) == -1) {
103 logger.log_error("Detach from PID %d error: %s",
104 pid, std::system_category().default_error_condition(errno).message());
109 void GetRegs(user_regs_struct *registers, pid_t pid) const {
110 #if defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
111 struct iovec iov = { registers, sizeof *registers };
112 if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1)
114 if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1)
116 throw std::system_error(errno, std::system_category(),
117 "failed to get registers for: " + std::to_string(pid));
120 std::vector<char> GetAuxv() {
121 auto auxv_name = "/proc/" + std::to_string(m_pid) + "/auxv";
124 file.open(auxv_name, std::ios::binary);
126 throw std::system_error(errno, std::system_category(),
127 "failed to open auxv file " + auxv_name);
129 std::vector<char> auxv(std::istreambuf_iterator<char>{file}, {});
133 void OpenCoredump(const std::string &path) {
134 m_core_file.open(path, std::ios::binary);
136 throw std::system_error(errno, std::system_category(),
137 "failed to open coredump file " + path);
140 void CloseCoredump() {
144 const std::vector<std::shared_ptr<MapRecord>> ReadMaps() {
147 return maps.Records();
150 void SaveRegsToPrStatusFd(const user_regs_struct ®isters) {
153 memset(&prstatus, 0, sizeof(prstatus));
154 memcpy(&prstatus.pr_reg, ®isters, sizeof(prstatus.pr_reg));
155 prstatus.pr_pid = m_pid;
157 map_buff.first = mmap(nullptr, sizeof(prstatus),
158 PROT_READ | PROT_WRITE,
162 if (map_buff.first == MAP_FAILED)
164 map_buff.second = sizeof prstatus;
166 memcpy(static_cast <char *> (map_buff.first), &prstatus, sizeof(prstatus));
169 void AddNotes(const std::vector<std::shared_ptr<MapRecord>> &records) {
170 user_regs_struct registers;
171 GetRegs(®isters, m_pid);
173 if (prstatus_fd >= 0)
174 SaveRegsToPrStatusFd(registers);
176 m_registers[m_pid] = registers;
177 m_core.AddPRSTATUSNote(registers, m_pid);
179 for (const auto &pid : m_tpids) {
182 GetRegs(®isters, pid);
184 m_registers[pid] = registers;
185 m_core.AddPRSTATUSNote(registers, pid);
188 m_core.AddFILENote(records);
189 std::vector<char> auxv = GetAuxv();
190 m_core.AddAUXVNote(auxv);
193 void MarkImportantRegions(const std::vector<std::shared_ptr<MapRecord>> &records) {
196 std::string GetExePath(const pid_t pid) {
198 std::string exe_path = std::string("/proc/" + std::to_string(pid) + "/exe");
200 if ((path_len = readlink(exe_path.c_str(), BUFF, sizeof(BUFF))) == -1) {
201 std::cout << "GetExePath error from" << exe_path << std::endl;
202 throw std::system_error(errno, std::system_category(), "GetExePath() for " + exe_path + " failed");
204 return std::string(BUFF, path_len);
207 bool DumpCore(const std::string &path, bool minicore) {
211 Guardian guard_open_coredump([this, &path]() { OpenCoredump(path);},
212 [this]() { CloseCoredump(); });
213 Guardian guard_attach([this]() { Attach(); },
214 [this]() { Detach(); });
216 const std::vector<std::shared_ptr<MapRecord>> records = ReadMaps();
219 MarkImportantRegions(records);
220 m_core.ImportMapRecords(records);
221 m_core.SaveELFHeader(m_core_file);
222 m_core.SaveProgramHeadersTable(m_core_file);
223 m_core.SaveNotes(m_core_file);
224 std::string mem_path = std::string("/proc/" + std::to_string(m_pid) + "/mem");
225 int mem_fd = open(mem_path.c_str(), O_RDONLY);
227 throw std::system_error(errno, std::system_category(), "open() for " + mem_path + " failed");
228 m_core.SaveLoadable(mem_fd, m_core_file, minicore);
230 m_core.SaveAUXVData(GetExePath(m_pid), mem_fd, m_core_file);
231 m_core.SavePthreadList(mem_fd, m_core_file, m_pid);
232 m_core.SaveStacks(mem_fd, m_core_file, m_tpids, m_registers);
235 } catch (std::system_error &e) {
236 logger.log_error("DumpCore: %s", e.what());
244 #endif // LIVEDUMPER_HPP__