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>
32 #include <asm/ptrace.h>
46 std::vector<pid_t> m_tpids;
47 std::map<pid_t,user_regs_struct> m_registers;
49 std::ofstream m_core_file;
51 std::pair <void *, size_t> map_buff;
54 explicit LiveDumper(const pid_t pid) : m_pid(pid), map_buff(MAP_FAILED, 0) {}
57 if (map_buff.first != MAP_FAILED)
58 munmap(map_buff.first, map_buff.second);
64 std::string task_dir = "/proc/" + std::to_string(m_pid) + "/task";
66 DIR *dir = opendir(task_dir.c_str());
68 throw std::system_error(errno, std::system_category(), "failed to open directory: " + task_dir);
71 while ((ent = readdir(dir)) != nullptr) {
72 if (ent->d_type == DT_DIR &&
73 strncmp(ent->d_name, ".", 1) != 0)
74 m_tpids.push_back(std::strtol(ent->d_name, 0, 10));
79 void AttachTo(const pid_t pid) const {
80 if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) == -1)
81 throw std::system_error(errno, std::system_category(), "failed to attach to: " + std::to_string(pid));
84 void setPrstatusFd(const int prstatus_fd) {
85 this->prstatus_fd = prstatus_fd;
91 waitpid(m_pid, nullptr, 0);
95 for (const auto &pid : m_tpids) {
96 if (pid == m_pid) continue;
101 void Detach() const {
102 for (const auto &pid : m_tpids) {
103 if (ptrace(PTRACE_DETACH, pid, nullptr, nullptr) == -1) {
104 logger.log_error("Detach from PID %d error: %s",
105 pid, std::system_category().default_error_condition(errno).message());
110 void GetRegs(user_regs_struct *registers, pid_t pid) const {
111 #if defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
112 struct iovec iov = { registers, sizeof *registers };
113 if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1)
115 if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1)
117 throw std::system_error(errno, std::system_category(),
118 "failed to get registers for: " + std::to_string(pid));
121 std::vector<char> GetAuxv() {
122 auto auxv_name = "/proc/" + std::to_string(m_pid) + "/auxv";
125 file.open(auxv_name, std::ios::binary);
127 throw std::system_error(errno, std::system_category(),
128 "failed to open auxv file " + auxv_name);
130 std::vector<char> auxv(std::istreambuf_iterator<char>{file}, {});
134 void OpenCoredump(const std::string &path) {
135 m_core_file.open(path, std::ios::binary);
137 throw std::system_error(errno, std::system_category(),
138 "failed to open coredump file " + path);
141 void CloseCoredump() {
145 const std::vector<std::shared_ptr<MapRecord>> ReadMaps() {
148 return maps.Records();
151 void SaveRegsToPrStatusFd(const user_regs_struct ®isters) {
154 memset(&prstatus, 0, sizeof(prstatus));
155 memcpy(&prstatus.pr_reg, ®isters, sizeof(prstatus.pr_reg));
156 prstatus.pr_pid = m_pid;
158 map_buff.first = mmap(nullptr, sizeof(prstatus),
159 PROT_READ | PROT_WRITE,
163 if (map_buff.first == MAP_FAILED)
165 map_buff.second = sizeof prstatus;
167 memcpy(static_cast <char *> (map_buff.first), &prstatus, sizeof(prstatus));
170 void AddNotes(const std::vector<std::shared_ptr<MapRecord>> &records) {
171 user_regs_struct registers;
172 GetRegs(®isters, m_pid);
174 if (prstatus_fd >= 0)
175 SaveRegsToPrStatusFd(registers);
177 m_registers[m_pid] = registers;
178 m_core.AddPRSTATUSNote(registers, m_pid);
180 for (const auto &pid : m_tpids) {
183 GetRegs(®isters, pid);
185 m_registers[pid] = registers;
186 m_core.AddPRSTATUSNote(registers, pid);
189 m_core.AddFILENote(records);
190 std::vector<char> auxv = GetAuxv();
191 m_core.AddAUXVNote(auxv);
194 void MarkImportantRegions(const std::vector<std::shared_ptr<MapRecord>> &records) {
197 std::string GetExePath(const pid_t pid) {
199 std::string exe_path = std::string("/proc/" + std::to_string(pid) + "/exe");
201 if ((path_len = readlink(exe_path.c_str(), BUFF, sizeof(BUFF))) == -1) {
202 std::cout << "GetExePath error from" << exe_path << std::endl;
203 throw std::system_error(errno, std::system_category(), "GetExePath() for " + exe_path + " failed");
205 return std::string(BUFF, path_len);
208 bool DumpCore(const std::string &path, bool minicore) {
212 Guardian guard_open_coredump([this, &path]() { OpenCoredump(path);},
213 [this]() { CloseCoredump(); });
214 Guardian guard_attach([this]() { Attach(); },
215 [this]() { Detach(); });
217 const std::vector<std::shared_ptr<MapRecord>> records = ReadMaps();
220 MarkImportantRegions(records);
221 m_core.ImportMapRecords(records);
222 m_core.SaveELFHeader(m_core_file);
223 m_core.SaveProgramHeadersTable(m_core_file);
224 m_core.SaveNotes(m_core_file);
225 std::string mem_path = std::string("/proc/" + std::to_string(m_pid) + "/mem");
226 int mem_fd = open(mem_path.c_str(), O_RDONLY);
228 throw std::system_error(errno, std::system_category(), "open() for " + mem_path + " failed");
229 m_core.SaveLoadable(mem_fd, m_core_file, minicore);
231 m_core.SaveAUXVData(GetExePath(m_pid), mem_fd, m_core_file);
232 m_core.SavePthreadList(mem_fd, m_core_file, m_pid);
233 m_core.SaveStacks(mem_fd, m_core_file, m_tpids, m_registers);
236 } catch (std::system_error &e) {
237 logger.log_error("DumpCore: %s", e.what());
245 #endif // LIVEDUMPER_HPP__