dump_process.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <sys/ptrace.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #include <unistd.h>
  8. #include <limits>
  9. #include <string>
  10. #include <vector>
  11. #include "base/debug/proc_maps_linux.h"
  12. #include "base/files/file.h"
  13. #include "base/files/file_path.h"
  14. #include "base/files/file_util.h"
  15. #include "base/format_macros.h"
  16. #include "base/logging.h"
  17. #include "base/posix/eintr_wrapper.h"
  18. #include "base/strings/string_number_conversions.h"
  19. #include "base/strings/string_util.h"
  20. #include "base/strings/stringprintf.h"
  21. namespace {
  22. using base::debug::MappedMemoryRegion;
  23. constexpr size_t kPageSize = 1 << 12;
  24. // See https://www.kernel.org/doc/Documentation/vm/pagemap.txt.
  25. struct PageMapEntry {
  26. uint64_t pfn_or_swap : 55;
  27. uint64_t soft_dirty : 1;
  28. uint64_t exclusively_mapped : 1;
  29. uint64_t unused : 4;
  30. uint64_t file_mapped_or_shared_anon : 1;
  31. uint64_t swapped : 1;
  32. uint64_t present : 1;
  33. };
  34. static_assert(sizeof(PageMapEntry) == sizeof(uint64_t), "Wrong bitfield size");
  35. // Calls ptrace() on a process, and detaches in the destructor.
  36. class ScopedPtracer {
  37. public:
  38. ScopedPtracer(pid_t pid) : pid_(pid), is_attached_(false) {
  39. // ptrace() delivers a SIGSTOP signal to one thread in the target process,
  40. // unless it is already stopped. Since we want to stop the whole process,
  41. // kill() it first.
  42. if (kill(pid, SIGSTOP)) {
  43. PLOG(ERROR) << "Cannot stop the process group of " << pid;
  44. return;
  45. }
  46. if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr)) {
  47. PLOG(ERROR) << "Unable to attach to " << pid;
  48. return;
  49. }
  50. // ptrace(PTRACE_ATTACH) sends a SISTOP signal to the process, need to wait
  51. // for it.
  52. int status;
  53. pid_t ret = HANDLE_EINTR(waitpid(pid, &status, 0));
  54. if (ret != pid) {
  55. PLOG(ERROR) << "Waiting for the process failed";
  56. return;
  57. }
  58. if (!WIFSTOPPED(status)) {
  59. LOG(ERROR) << "The process is not stopped";
  60. ptrace(PTRACE_DETACH, pid, 0, 0);
  61. return;
  62. }
  63. is_attached_ = true;
  64. }
  65. ~ScopedPtracer() {
  66. if (!is_attached_)
  67. return;
  68. if (ptrace(PTRACE_DETACH, pid_, 0, 0)) {
  69. PLOG(ERROR) << "Cannot detach from " << pid_;
  70. }
  71. pid_t process_group_id = getpgid(pid_);
  72. if (killpg(process_group_id, SIGCONT)) {
  73. PLOG(ERROR) << "Cannot resume the process " << pid_;
  74. return;
  75. }
  76. }
  77. bool IsAttached() const { return is_attached_; }
  78. private:
  79. pid_t pid_;
  80. bool is_attached_;
  81. };
  82. bool ParseProcMaps(pid_t pid, std::vector<MappedMemoryRegion>* regions) {
  83. std::string path = base::StringPrintf("/proc/%d/maps", pid);
  84. std::string proc_maps;
  85. bool ok = base::ReadFileToString(base::FilePath(path), &proc_maps);
  86. if (!ok) {
  87. LOG(ERROR) << "Cannot read " << path;
  88. return false;
  89. }
  90. ok = base::debug::ParseProcMaps(proc_maps, regions);
  91. if (!ok) {
  92. LOG(ERROR) << "Cannot parse " << path;
  93. return false;
  94. }
  95. return true;
  96. }
  97. // Keep anonynmous rw-p regions.
  98. bool ShouldDump(const MappedMemoryRegion& region) {
  99. const auto rw_p = MappedMemoryRegion::READ | MappedMemoryRegion::WRITE |
  100. MappedMemoryRegion::PRIVATE;
  101. if (region.permissions != rw_p)
  102. return false;
  103. if (base::StartsWith(region.path, "/", base::CompareCase::SENSITIVE) ||
  104. base::StartsWith(region.path, "[stack]", base::CompareCase::SENSITIVE)) {
  105. return false;
  106. }
  107. return true;
  108. }
  109. base::File OpenProcPidFile(const char* filename, pid_t pid) {
  110. std::string path = base::StringPrintf("/proc/%d/%s", pid, filename);
  111. auto file = base::File(base::FilePath(path),
  112. base::File::FLAG_OPEN | base::File::FLAG_READ);
  113. if (!file.IsValid()) {
  114. PLOG(ERROR) << "Cannot open " << path;
  115. }
  116. return file;
  117. }
  118. bool DumpRegion(const MappedMemoryRegion& region,
  119. pid_t pid,
  120. base::File* proc_mem,
  121. base::File* proc_pagemap) {
  122. size_t size_in_pages = (region.end - region.start) / kPageSize;
  123. std::string output_path = base::StringPrintf("%d-%" PRIuS "-%" PRIuS ".dump",
  124. pid, region.start, region.end);
  125. base::File output_file(base::FilePath(output_path),
  126. base::File::FLAG_WRITE | base::File::FLAG_CREATE);
  127. if (!output_file.IsValid()) {
  128. PLOG(ERROR) << "Cannot open " << output_path;
  129. return false;
  130. }
  131. std::string metadata_path = output_path + std::string(".metadata");
  132. base::File metadata_file(base::FilePath(metadata_path),
  133. base::File::FLAG_WRITE | base::File::FLAG_CREATE);
  134. if (!metadata_file.IsValid()) {
  135. PLOG(ERROR) << "Cannot open " << metadata_path;
  136. return false;
  137. }
  138. // Dump metadata.
  139. // Important: Metadata must be dumped before the data, as reading from
  140. // /proc/pid/mem will move the data back from swap, so dumping metadata
  141. // later would not show anything in swap.
  142. // This also means that dumping the same process twice will result in
  143. // inaccurate metadata.
  144. for (size_t i = 0; i < size_in_pages; ++i) {
  145. // See https://www.kernel.org/doc/Documentation/vm/pagemap.txt
  146. // 64 bits per page.
  147. int64_t pagemap_offset =
  148. ((region.start / kPageSize) + i) * sizeof(PageMapEntry);
  149. PageMapEntry entry;
  150. proc_pagemap->Seek(base::File::FROM_BEGIN, pagemap_offset);
  151. int size_read = proc_pagemap->ReadAtCurrentPos(
  152. reinterpret_cast<char*>(&entry), sizeof(PageMapEntry));
  153. if (size_read != sizeof(PageMapEntry)) {
  154. PLOG(ERROR) << "Cannot read from /proc/pid/pagemap at offset "
  155. << pagemap_offset;
  156. return false;
  157. }
  158. std::string metadata = base::StringPrintf(
  159. "%c%c\n", entry.present ? '1' : '0', entry.swapped ? '1' : '0');
  160. metadata_file.WriteAtCurrentPos(metadata.c_str(), metadata.size());
  161. }
  162. // Writing data page by page to avoid allocating too much memory.
  163. std::vector<char> buffer(kPageSize);
  164. for (size_t i = 0; i < size_in_pages; ++i) {
  165. uint64_t address = region.start + i * kPageSize;
  166. // Works because the upper half of the address space is reserved for the
  167. // kernel on at least ARM64 and x86_64 bit architectures.
  168. CHECK(address <= std::numeric_limits<int64_t>::max());
  169. proc_mem->Seek(base::File::FROM_BEGIN, static_cast<int64_t>(address));
  170. int size_read = proc_mem->ReadAtCurrentPos(&buffer[0], kPageSize);
  171. if (size_read != kPageSize) {
  172. PLOG(ERROR) << "Cannot read from /proc/pid/mem at offset " << address;
  173. return false;
  174. }
  175. int64_t output_offset = i * kPageSize;
  176. int size_written = output_file.Write(output_offset, &buffer[0], kPageSize);
  177. if (size_written != kPageSize) {
  178. PLOG(ERROR) << "Cannot write to output file";
  179. return false;
  180. }
  181. }
  182. return true;
  183. }
  184. // Dumps the content of all the anonymous rw-p mappings in a given process to
  185. // disk.
  186. bool DumpMappings(pid_t pid) {
  187. LOG(INFO) << "Attaching to " << pid;
  188. // ptrace() is not required to read the process's memory, but the permissions
  189. // to attach to the target process is.
  190. // Attach anyway to make it clearer when this fails.
  191. ScopedPtracer tracer(pid);
  192. if (!tracer.IsAttached())
  193. return false;
  194. LOG(INFO) << "Reading /proc/pid/maps";
  195. std::vector<base::debug::MappedMemoryRegion> regions;
  196. bool ok = ParseProcMaps(pid, &regions);
  197. if (!ok)
  198. return false;
  199. base::File proc_mem = OpenProcPidFile("mem", pid);
  200. if (!proc_mem.IsValid())
  201. return false;
  202. base::File proc_pagemap = OpenProcPidFile("pagemap", pid);
  203. if (!proc_pagemap.IsValid())
  204. return false;
  205. for (const auto& region : regions) {
  206. if (!ShouldDump(region))
  207. continue;
  208. std::string message =
  209. base::StringPrintf("%" PRIuS "-%" PRIuS " (size %" PRIuS ")",
  210. region.start, region.end, region.end - region.start);
  211. LOG(INFO) << "Dumping " << message;
  212. ok = DumpRegion(region, pid, &proc_mem, &proc_pagemap);
  213. if (!ok) {
  214. LOG(WARNING) << "Failed to dump region";
  215. }
  216. }
  217. return true;
  218. }
  219. } // namespace
  220. int main(int argc, char** argv) {
  221. CHECK(sysconf(_SC_PAGESIZE) == kPageSize);
  222. if (argc != 2) {
  223. LOG(ERROR) << "Usage: " << argv[0] << " <pid>";
  224. return 1;
  225. }
  226. pid_t pid;
  227. bool ok = base::StringToInt(argv[1], &pid);
  228. if (!ok) {
  229. LOG(ERROR) << "Cannot parse PID";
  230. return 1;
  231. }
  232. ok = DumpMappings(pid);
  233. return ok ? 0 : 1;
  234. }