01bcb1a5cce7500938f2d84e2c7a02bede4cc775
[platform/core/system/crash-worker.git] / src / livedumper / livedumper.hpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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 #ifndef LIVEDUMPER_HPP__
19 #define LIVEDUMPER_HPP__
20
21 #include "core.hpp"
22 #include "helpers.hpp"
23 #include "prochandle.hpp"
24 #include "log.hpp"
25 #include "maps.hpp"
26
27 #include <dirent.h>
28 #include <limits.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/ptrace.h>
32 #include <asm/ptrace.h>
33 #include <sys/wait.h>
34
35 #include <fstream>
36 #include <iostream>
37 #include <stdexcept>
38 #include <string>
39 #include <vector>
40 #include <map>
41
42 template <typename T>
43 class LiveDumper {
44  private:
45         pid_t m_pid;
46         std::vector<pid_t> m_tpids;
47         std::map<pid_t,user_regs_struct> m_registers;
48         Core<T> m_core;
49         std::ofstream m_core_file;
50         int prstatus_fd = -1;
51         std::pair <void *, size_t> map_buff;
52
53  public:
54         explicit LiveDumper(const pid_t pid) : m_pid(pid), map_buff(MAP_FAILED, 0) {}
55
56         ~ LiveDumper () {
57                 if (map_buff.first != MAP_FAILED)
58                         munmap(map_buff.first, map_buff.second);
59         }
60
61         void CollectTpids() {
62                 m_tpids.clear();
63
64                 std::string task_dir = "/proc/" + std::to_string(m_pid) + "/task";
65
66                 DIR *dir = opendir(task_dir.c_str());
67                 if (dir == nullptr)
68                         throw std::system_error(errno, std::system_category(), "failed to open directory: " + task_dir);
69
70                 dirent *ent;
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));
75                 }
76                 closedir(dir);
77         }
78
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));
82         }
83
84         void setPrstatusFd(const int prstatus_fd) {
85                 this->prstatus_fd = prstatus_fd;
86         }
87
88         void Attach() {
89                 AttachTo(m_pid);
90
91                 waitpid(m_pid, nullptr, 0);
92
93                 CollectTpids();
94
95                 for (const auto &pid : m_tpids) {
96                         if (pid == m_pid) continue;
97                         AttachTo(pid);
98                 }
99         }
100
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());
106                         }
107                 }
108         }
109
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)
114 #else
115                 if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1)
116 #endif
117                         throw std::system_error(errno, std::system_category(),
118                                                                         "failed to get registers for: " + std::to_string(pid));
119         }
120
121         std::vector<char> GetAuxv() {
122                 auto auxv_name = "/proc/" + std::to_string(m_pid) + "/auxv";
123                 std::ifstream file;
124
125                 file.open(auxv_name, std::ios::binary);
126                 if (!file)
127                         throw std::system_error(errno, std::system_category(),
128                                                                         "failed to open auxv file " + auxv_name);
129
130                 std::vector<char> auxv(std::istreambuf_iterator<char>{file}, {});
131                 return auxv;
132         }
133
134         void OpenCoredump(const std::string &path) {
135                 m_core_file.open(path, std::ios::binary);
136                 if (!m_core_file)
137                         throw std::system_error(errno, std::system_category(),
138                                                                         "failed to open coredump file " + path);
139         }
140
141         void CloseCoredump() {
142                 m_core_file.close();
143         }
144
145         const std::vector<std::shared_ptr<MapRecord>> ReadMaps() {
146                 Maps maps(m_pid);
147                 maps.Parse();
148                 return maps.Records();
149         }
150
151         void SaveRegsToPrStatusFd(const user_regs_struct &registers) {
152                 prstatus_t prstatus;
153
154                 memset(&prstatus, 0, sizeof(prstatus));
155                 memcpy(&prstatus.pr_reg, &registers, sizeof(prstatus.pr_reg));
156                 prstatus.pr_pid = m_pid;
157
158                 map_buff.first = mmap(nullptr, sizeof(prstatus),
159                                                          PROT_READ | PROT_WRITE,
160                                                          MAP_SHARED,
161                                                          prstatus_fd,
162                                                          0);
163                 if (map_buff.first == MAP_FAILED)
164                         return;
165                 map_buff.second = sizeof prstatus;
166
167                 memcpy(static_cast <char *> (map_buff.first), &prstatus, sizeof(prstatus));
168         }
169
170         void AddNotes(const std::vector<std::shared_ptr<MapRecord>> &records) {
171                 user_regs_struct registers;
172                 GetRegs(&registers, m_pid);
173
174                 if (prstatus_fd >= 0)
175                         SaveRegsToPrStatusFd(registers);
176
177                 m_registers[m_pid] = registers;
178                 m_core.AddPRSTATUSNote(registers, m_pid);
179
180                 for (const auto &pid : m_tpids) {
181                         if (pid == m_pid)
182                                 continue;
183                         GetRegs(&registers, pid);
184
185                         m_registers[pid] = registers;
186                         m_core.AddPRSTATUSNote(registers, pid);
187                 }
188
189                 m_core.AddFILENote(records);
190                 std::vector<char> auxv = GetAuxv();
191                 m_core.AddAUXVNote(auxv);
192         }
193
194         void MarkImportantRegions(const std::vector<std::shared_ptr<MapRecord>> &records) {
195         }
196
197         std::string GetExePath(const pid_t pid) {
198                 char BUFF[PATH_MAX];
199                 std::string exe_path = std::string("/proc/" + std::to_string(pid) + "/exe");
200                 ssize_t path_len;
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");
204                 }
205                 return std::string(BUFF, path_len);
206         }
207
208         bool DumpCore(const std::string &path, bool minicore) {
209                 bool result = true;
210
211                 try {
212                         Guardian guard_open_coredump([this, &path]() { OpenCoredump(path);},
213                                          [this]() { CloseCoredump(); });
214                         Guardian guard_attach([this]() { Attach(); },
215                                          [this]() { Detach(); });
216
217                         const std::vector<std::shared_ptr<MapRecord>> records = ReadMaps();
218
219                         AddNotes(records);
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);
227                         if (mem_fd == -1)
228                                 throw std::system_error(errno, std::system_category(), "open() for " + mem_path + " failed");
229                         m_core.SaveLoadable(mem_fd, m_core_file, minicore);
230                         if (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);
234                         }
235                         close(mem_fd);
236                 } catch (std::system_error &e) {
237                         logger.log_error("DumpCore: %s", e.what());
238                         result = false;
239                 }
240
241                 return result;
242         }
243 };
244
245 #endif  // LIVEDUMPER_HPP__