1 /*
2 * Copyright (C) 2017 The Android Open Source Project
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 #include <ctype.h>
18 #include <inttypes.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/mman.h>
23 #include <unistd.h>
24
25 #include <algorithm>
26 #include <filesystem>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <unordered_map>
31 #include <utility>
32 #include <vector>
33
34 #include <unwindstack/AndroidUnwinder.h>
35 #include <unwindstack/MapInfo.h>
36 #include <unwindstack/Maps.h>
37 #include <unwindstack/Memory.h>
38 #include <unwindstack/Regs.h>
39 #include "utils/ProcessTracer.h"
40
41 #include <android-base/file.h>
42 #include <android-base/parseint.h>
43 #include <android-base/stringprintf.h>
44
45 namespace {
46 constexpr pid_t kMinPid = 1;
47 constexpr int kAllCmdOptionsParsed = -1;
48
usage(int exit_code)49 int usage(int exit_code) {
50 fprintf(stderr, "USAGE: unwind_for_offline [-t] [-e FILE] [-f[FILE]] <PID>\n\n");
51 fprintf(stderr, "OPTIONS:\n");
52 fprintf(stderr, "-t\n");
53 fprintf(stderr, " Dump offline snapshot for all threads of <PID>.\n");
54 fprintf(stderr, "-e FILE\n");
55 fprintf(stderr, " If FILE is a valid ELF file included in /proc/<PID>/maps,\n");
56 fprintf(stderr, " unwind_for_offline will wait until the current frame (PC)\n");
57 fprintf(stderr, " lies within the .so file given by FILE. FILE should be\n");
58 fprintf(stderr, " base name of the path (the component following the final\n");
59 fprintf(stderr, " '/') rather than the fully qualified path.\n");
60 fprintf(stderr, "-f [FILE]\n");
61 fprintf(stderr, " Write info (e.g. frames and stack range) logs to a file\n");
62 fprintf(stderr, " rather than to the stdout/stderr. If FILE is not\n");
63 fprintf(stderr, " specified, the output file will be named 'output.txt'.\n");
64 return exit_code;
65 }
66
EnsureProcInDesiredElf(const std::string & elf_name,unwindstack::ProcessTracer & proc)67 bool EnsureProcInDesiredElf(const std::string& elf_name, unwindstack::ProcessTracer& proc) {
68 if (proc.UsesSharedLibrary(proc.pid(), elf_name)) {
69 printf("Confirmed pid %d does use %s. Waiting for PC to lie within %s...\n", proc.pid(),
70 elf_name.c_str(), elf_name.c_str());
71 if (!proc.StopInDesiredElf(elf_name)) return false;
72 } else {
73 fprintf(stderr, "Process %d does not use library %s.\n", proc.pid(), elf_name.c_str());
74 return false;
75 }
76 return true;
77 }
78
CreateAndChangeDumpDir(std::filesystem::path thread_dir,pid_t tid,bool is_main_thread)79 bool CreateAndChangeDumpDir(std::filesystem::path thread_dir, pid_t tid, bool is_main_thread) {
80 std::string dir_name = std::to_string(tid);
81 if (is_main_thread) dir_name += "_main-thread";
82 thread_dir /= dir_name;
83 if (!std::filesystem::create_directory(thread_dir)) {
84 fprintf(stderr, "Failed to create directory for tid %d\n", tid);
85 return false;
86 }
87 std::filesystem::current_path(thread_dir);
88 return true;
89 }
90
SaveRegs(unwindstack::Regs * regs)91 bool SaveRegs(unwindstack::Regs* regs) {
92 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "we+"), &fclose);
93 if (fp == nullptr) {
94 perror("Failed to create file regs.txt");
95 return false;
96 }
97 regs->IterateRegisters([&fp](const char* name, uint64_t value) {
98 fprintf(fp.get(), "%s: %" PRIx64 "\n", name, value);
99 });
100
101 return true;
102 }
103
SaveStack(pid_t tid,const std::vector<std::pair<uint64_t,uint64_t>> & stacks,FILE * output_fp)104 bool SaveStack(pid_t tid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks,
105 FILE* output_fp) {
106 for (size_t i = 0; i < stacks.size(); i++) {
107 std::string file_name;
108 if (stacks.size() != 1) {
109 file_name = "stack" + std::to_string(i) + ".data";
110 } else {
111 file_name = "stack.data";
112 }
113
114 // Do this first, so if it fails, we don't create the file.
115 uint64_t sp_start = stacks[i].first;
116 uint64_t sp_end = stacks[i].second;
117 std::vector<uint8_t> buffer(sp_end - sp_start);
118 auto process_memory = unwindstack::Memory::CreateProcessMemory(tid);
119 if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
120 fprintf(stderr, "Unable to read stack data.\n");
121 return false;
122 }
123
124 fprintf(output_fp, "\nSaving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
125
126 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "we+"), &fclose);
127 if (fp == nullptr) {
128 perror("Failed to create stack.data");
129 return false;
130 }
131
132 size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
133 if (bytes != sizeof(sp_start)) {
134 fprintf(stderr, "Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n",
135 sizeof(sp_start), bytes);
136 return false;
137 }
138
139 bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
140 if (bytes != buffer.size()) {
141 fprintf(stderr, "Failed to write all stack data: stack size %zu, written %zu\n",
142 buffer.size(), bytes);
143 return false;
144 }
145 }
146
147 return true;
148 }
149
CopyElf(unwindstack::MapInfo * map_info,std::string * name)150 bool CopyElf(unwindstack::MapInfo* map_info, std::string* name) {
151 std::string cur_name = android::base::Basename(map_info->name());
152
153 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(map_info->name().c_str(), "re"), &fclose);
154 if (fp == nullptr) {
155 perror((std::string("Cannot open ") + map_info->name().c_str()).c_str());
156 return false;
157 }
158
159 std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "we+"), &fclose);
160 if (output == nullptr) {
161 perror((std::string("Cannot create file " + cur_name)).c_str());
162 return false;
163 }
164 std::vector<uint8_t> buffer(10000);
165 size_t bytes;
166 while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
167 size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
168 if (bytes_written != bytes) {
169 fprintf(stderr, "Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
170 bytes_written);
171 return false;
172 }
173 }
174
175 *name = std::move(cur_name);
176 return true;
177 }
178
CreateElfFromMemory(pid_t tid,unwindstack::MapInfo * map_info,std::string * name)179 bool CreateElfFromMemory(pid_t tid, unwindstack::MapInfo* map_info, std::string* name) {
180 std::string cur_name;
181 if (map_info->name().empty()) {
182 cur_name = android::base::StringPrintf("anonymous_%" PRIx64, map_info->start());
183 } else {
184 cur_name = android::base::StringPrintf(
185 "%s_%" PRIx64, android::base::Basename(map_info->name()).c_str(), map_info->start());
186 }
187
188 // If this is a mapped in file, it might not be possible to read the entire
189 // map, so read all that is readable.
190 std::vector<uint8_t> buffer(map_info->end() - map_info->start());
191 auto memory = unwindstack::Memory::CreateProcessMemory(tid);
192 size_t bytes = memory->Read(map_info->start(), buffer.data(), buffer.size());
193 if (bytes == 0) {
194 fprintf(stderr, "Cannot read data from address %" PRIx64 " length %zu\n", map_info->start(),
195 buffer.size());
196 return false;
197 }
198
199 std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "we+"), &fclose);
200 if (output == nullptr) {
201 perror((std::string("Cannot create ") + cur_name).c_str());
202 return false;
203 }
204
205 size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
206 if (bytes_written != bytes) {
207 fprintf(stderr, "Failed to write all data to file: bytes read %zu, written %zu\n", bytes,
208 bytes_written);
209 return false;
210 }
211
212 *name = std::move(cur_name);
213
214 return true;
215 }
216
CopyMapInfo(pid_t tid,unwindstack::MapInfo * map_info,std::unordered_map<std::string,std::string> & copied_files,std::string * name)217 bool CopyMapInfo(pid_t tid, unwindstack::MapInfo* map_info,
218 std::unordered_map<std::string, std::string>& copied_files, std::string* name) {
219 auto entry = copied_files.find(map_info->name());
220 if (entry != copied_files.end()) {
221 // Already copied the file, do nothing.
222 *name = entry->second;
223 return true;
224 }
225
226 if (CopyElf(map_info, name)) {
227 copied_files[map_info->name()] = *name;
228 return true;
229 }
230
231 if (CreateElfFromMemory(tid, map_info, name)) {
232 return true;
233 }
234
235 fprintf(stderr, "Cannot save memory or file for map ");
236 if (!map_info->name().empty()) {
237 fprintf(stderr, "%s\n", map_info->name().c_str());
238 } else {
239 fprintf(stderr, "anonymous:%" PRIx64 "\n", map_info->start());
240 }
241 return false;
242 }
243
WriteMapEntry(FILE * fp,unwindstack::MapInfo * map_info,const std::string & name)244 void WriteMapEntry(FILE* fp, unwindstack::MapInfo* map_info, const std::string& name) {
245 char perms[5] = {"---p"};
246 if (map_info->flags() & PROT_READ) {
247 perms[0] = 'r';
248 }
249 if (map_info->flags() & PROT_WRITE) {
250 perms[1] = 'w';
251 }
252 if (map_info->flags() & PROT_EXEC) {
253 perms[2] = 'x';
254 }
255 fprintf(fp, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map_info->start(), map_info->end(),
256 perms, map_info->offset());
257 if (!name.empty()) {
258 fprintf(fp, " %s", name.c_str());
259 }
260 fprintf(fp, "\n");
261 }
262
SaveMapInfo(FILE * maps_fp,pid_t tid,unwindstack::MapInfo * map_info,std::unordered_map<std::string,std::string> & copied_files)263 void SaveMapInfo(FILE* maps_fp, pid_t tid, unwindstack::MapInfo* map_info,
264 std::unordered_map<std::string, std::string>& copied_files) {
265 auto prev_info = map_info->GetPrevRealMap();
266 if (prev_info != nullptr) {
267 SaveMapInfo(maps_fp, tid, prev_info.get(), copied_files);
268 }
269 std::string map_name;
270 if (CopyMapInfo(tid, map_info, copied_files, &map_name)) {
271 WriteMapEntry(maps_fp, map_info, map_name);
272 }
273 }
274
SaveData(pid_t tid,const std::filesystem::path & cwd,bool is_main_thread,FILE * output_fp)275 bool SaveData(pid_t tid, const std::filesystem::path& cwd, bool is_main_thread, FILE* output_fp) {
276 fprintf(output_fp, "-------------------- tid = %d %s--------------------\n", tid,
277 is_main_thread ? "(main thread) " : "--------------");
278 unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(tid);
279 if (regs == nullptr) {
280 fprintf(stderr, "Unable to get remote reg data.\n");
281 return false;
282 }
283
284 if (!CreateAndChangeDumpDir(cwd, tid, is_main_thread)) {
285 return false;
286 }
287
288 // Save the current state of the registers.
289 if (!SaveRegs(regs)) {
290 return false;
291 }
292 uint64_t sp = regs->sp();
293
294 // Do an unwind so we know how much of the stack to save, and what
295 // elf files are involved.
296 unwindstack::AndroidRemoteUnwinder unwinder(tid);
297 unwindstack::AndroidUnwinderData data;
298 if (!unwinder.Unwind(regs, data)) {
299 fprintf(stderr, "Unable to unwind tid %d: %s\n", tid, data.GetErrorString().c_str());
300 return false;
301 }
302 data.DemangleFunctionNames();
303
304 std::vector<std::pair<uint64_t, uint64_t>> stacks;
305 unwindstack::Maps* maps = unwinder.GetMaps();
306 uint64_t sp_map_start = 0;
307 auto map_info = maps->Find(sp);
308 if (map_info != nullptr) {
309 stacks.emplace_back(std::make_pair(sp, map_info->end()));
310 sp_map_start = map_info->start();
311 }
312
313 std::unordered_map<uintptr_t, unwindstack::MapInfo*> map_infos;
314 for (const auto& frame : data.frames) {
315 auto map_info = maps->Find(frame.sp);
316 if (map_info != nullptr && sp_map_start != map_info->start()) {
317 stacks.emplace_back(std::make_pair(frame.sp, map_info->end()));
318 sp_map_start = map_info->start();
319 }
320 map_infos[reinterpret_cast<uintptr_t>(frame.map_info.get())] = frame.map_info.get();
321 }
322
323 for (const auto& frame : data.frames) {
324 fprintf(output_fp, "%s\n", unwinder.FormatFrame(frame).c_str());
325 }
326
327 if (!SaveStack(tid, stacks, output_fp)) {
328 return false;
329 }
330
331 std::unique_ptr<FILE, decltype(&fclose)> maps_fp(fopen("maps.txt", "we+"), &fclose);
332 if (maps_fp == nullptr) {
333 perror("Failed to create maps.txt");
334 return false;
335 }
336
337 std::vector<unwindstack::MapInfo*> sorted_map_infos(map_infos.size());
338 std::transform(map_infos.begin(), map_infos.end(), sorted_map_infos.begin(),
339 [](auto entry) { return entry.second; });
340 std::sort(sorted_map_infos.begin(), sorted_map_infos.end(),
341 [](auto a, auto b) { return b->start() > a->start(); });
342 std::unordered_map<std::string, std::string> copied_files;
343 for (auto& map_info : sorted_map_infos) {
344 SaveMapInfo(maps_fp.get(), tid, map_info, copied_files);
345 }
346
347 fprintf(output_fp, "------------------------------------------------------------------\n");
348 return true;
349 }
350 } // namespace
351
main(int argc,char ** argv)352 int main(int argc, char** argv) {
353 if (argc < 2) return usage(EXIT_FAILURE);
354
355 bool dump_threads = false;
356 std::string elf_name;
357 std::unique_ptr<FILE, decltype(&fclose)> output_fp(nullptr, &fclose);
358 int opt;
359 while ((opt = getopt(argc, argv, ":te:f::")) != kAllCmdOptionsParsed) {
360 switch (opt) {
361 case 't': {
362 dump_threads = true;
363 break;
364 }
365 case 'e': {
366 elf_name = optarg;
367 if (elf_name == "-f") {
368 fprintf(stderr, "Missing argument for option e.\n");
369 return usage(EXIT_FAILURE);
370 }
371 break;
372 }
373 case 'f': {
374 const std::string& output_filename = optarg != nullptr ? optarg : "output.txt";
375 if (optind == argc - 2) {
376 fprintf(stderr, "Ensure there is no space between '-f' and the filename provided.\n");
377 return usage(EXIT_FAILURE);
378 }
379 output_fp.reset(fopen(output_filename.c_str(), "ae"));
380 break;
381 }
382 case '?': {
383 if (isprint(optopt))
384 fprintf(stderr, "Unknown option `-%c'.\n", optopt);
385 else
386 fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
387 return usage(EXIT_FAILURE);
388 }
389 case ':': {
390 fprintf(stderr, "Missing arg for option %c.\n", optopt);
391 return usage(EXIT_FAILURE);
392 }
393 default: {
394 return usage(EXIT_FAILURE);
395 }
396 }
397 }
398 if (optind != argc - 1) return usage(EXIT_FAILURE);
399
400 pid_t pid;
401 if (!android::base::ParseInt(argv[optind], &pid, kMinPid, std::numeric_limits<pid_t>::max()))
402 return usage(EXIT_FAILURE);
403
404 unwindstack::ProcessTracer proc(pid, dump_threads);
405 if (!proc.Stop()) return EXIT_FAILURE;
406 if (!elf_name.empty()) {
407 if (!EnsureProcInDesiredElf(elf_name, proc)) return EXIT_FAILURE;
408 }
409 if (!output_fp) output_fp.reset(stdout);
410 std::filesystem::path cwd = std::filesystem::current_path();
411
412 if (!proc.Attach(proc.pid())) return EXIT_FAILURE;
413 if (!SaveData(proc.pid(), cwd, /*is_main_thread=*/proc.IsTracingThreads(), output_fp.get()))
414 return EXIT_FAILURE;
415 if (!proc.Detach(proc.pid())) return EXIT_FAILURE;
416 for (const pid_t& tid : proc.tids()) {
417 if (!proc.Attach(tid)) return EXIT_FAILURE;
418 if (!SaveData(tid, cwd, /*is_main_thread=*/false, output_fp.get())) return EXIT_FAILURE;
419 if (!proc.Detach(tid)) return EXIT_FAILURE;
420 }
421
422 printf("\nSuccess!\n");
423 return EXIT_SUCCESS;
424 }
425