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