• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 <err.h>
18 #include <errno.h>
19 #include <inttypes.h>
20 #include <string.h>
21 
22 #include <cstddef>
23 #include <filesystem>
24 #include <fstream>
25 #include <iostream>
26 #include <memory>
27 #include <regex>
28 #include <sstream>
29 #include <string>
30 #include <tuple>
31 #include <utility>
32 #include <vector>
33 
34 #include <android-base/file.h>
35 #include <android-base/strings.h>
36 #include <zlib.h>
37 
38 #include <unwindstack/Arch.h>
39 #include <unwindstack/JitDebug.h>
40 #include <unwindstack/MachineArm.h>
41 #include <unwindstack/MachineArm64.h>
42 #include <unwindstack/MachineX86.h>
43 #include <unwindstack/MachineX86_64.h>
44 #include <unwindstack/Maps.h>
45 #include <unwindstack/Regs.h>
46 #include <unwindstack/RegsArm.h>
47 #include <unwindstack/RegsArm64.h>
48 #include <unwindstack/RegsX86.h>
49 #include <unwindstack/RegsX86_64.h>
50 #include <unwindstack/Unwinder.h>
51 
52 #include "Check.h"
53 #include "MemoryOffline.h"
54 #include "utils/MemoryFake.h"
55 
56 #include "OfflineUnwindUtils.h"
57 
58 namespace unwindstack {
59 
DecompressFiles(const std::string & directory)60 void DecompressFiles(const std::string& directory) {
61   namespace fs = std::filesystem;
62   for (const auto& file : fs::recursive_directory_iterator(directory)) {
63     fs::path src_path = file.path();
64     if (src_path.extension() == ".gz") {
65       fs::path dst_path = fs::path(src_path).replace_extension();  // Remove .gz extension.
66       if (!fs::exists(dst_path) || fs::last_write_time(src_path) > fs::last_write_time(dst_path)) {
67         gzFile src = gzopen(src_path.c_str(), "rb");
68         CHECK(src != nullptr);
69         fs::path tmp_path = fs::path(src_path).replace_extension("." + std::to_string(getpid()));
70         std::ofstream tmp(tmp_path);  // Temporary file to avoid races between unit tests.
71         char buffer[1024];
72         int size;
73         while ((size = gzread(src, buffer, sizeof(buffer))) > 0) {
74           tmp.write(buffer, size);
75         }
76         tmp.close();
77         gzclose(src);
78         fs::rename(tmp_path, dst_path);
79       }
80     }
81   }
82 }
83 
CreateLinks(const std::string & directory)84 void CreateLinks(const std::string& directory) {
85   namespace fs = std::filesystem;
86   for (const auto& file : fs::recursive_directory_iterator(directory)) {
87     fs::path src_path = file.path();
88     if (fs::is_regular_file(src_path) && src_path.filename() == "links.txt") {
89       std::string contents;
90       if (!android::base::ReadFileToString(src_path.c_str(), &contents)) {
91         errx(1, "Unable to read file: %s", src_path.c_str());
92       }
93       fs::path parent_path = src_path.parent_path();
94       std::vector<std::string> lines(android::base::Split(contents, "\n"));
95       for (auto line : lines) {
96         std::string trimmed_line(android::base::Trim(line));
97         if (trimmed_line.empty()) {
98           continue;
99         }
100 
101         std::vector<std::string> values(android::base::Split(trimmed_line, " "));
102         if (values.size() != 2) {
103           errx(1, "Invalid line in %s: line %s", src_path.c_str(), line.c_str());
104         }
105 
106         // Create the symlink if it doesn't already exist.
107         fs::path target(parent_path);
108         target /= fs::path(values[0]);
109         fs::path source(parent_path);
110         source /= fs::path(values[1]);
111         if (!fs::exists(source)) {
112           // Ignore any errors, if this is running at the same time
113           // in multiple processes, then this might fail.
114           std::error_code ec;
115           fs::create_symlink(target, source, ec);
116         }
117       }
118     }
119   }
120 }
121 
GetOfflineFilesDirectory()122 std::string GetOfflineFilesDirectory() {
123   std::string path = android::base::GetExecutableDirectory() + "/offline_files/";
124   DecompressFiles(path);
125   CreateLinks(path);
126   return path;
127 }
128 
DumpFrames(const Unwinder & unwinder)129 std::string DumpFrames(const Unwinder& unwinder) {
130   std::string str;
131   for (size_t i = 0; i < unwinder.NumFrames(); i++) {
132     str += unwinder.FormatFrame(i) + "\n";
133   }
134   return str;
135 }
136 
AddMemory(std::string file_name,MemoryOfflineParts * parts,std::string * error_msg)137 bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg) {
138   MemoryOffline* memory = new MemoryOffline;
139   if (!memory->Init(file_name.c_str(), 0)) {
140     std::stringstream err_stream;
141     err_stream << "Failed to add stack '" << file_name << "' to stack memory.";
142     *error_msg = err_stream.str();
143     return false;
144   }
145   parts->Add(memory);
146 
147   return true;
148 }
149 
GetRegs(const std::string & initial_sample_name) const150 Regs* OfflineUnwindUtils::GetRegs(const std::string& initial_sample_name) const {
151   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
152   std::string error_msg;
153   if (!IsValidUnwindSample(sample_name, &error_msg)) {
154     std::cerr << error_msg;
155     return nullptr;
156   }
157   return samples_.at(sample_name).regs.get();
158 }
159 
GetMaps(const std::string & initial_sample_name) const160 Maps* OfflineUnwindUtils::GetMaps(const std::string& initial_sample_name) const {
161   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
162   std::string error_msg;
163   if (!IsValidUnwindSample(sample_name, &error_msg)) {
164     std::cerr << error_msg;
165     return nullptr;
166   }
167   return samples_.at(sample_name).maps.get();
168 }
169 
GetProcessMemory(const std::string & initial_sample_name) const170 std::shared_ptr<Memory> OfflineUnwindUtils::GetProcessMemory(
171     const std::string& initial_sample_name) const {
172   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
173   std::string error_msg;
174   if (!IsValidUnwindSample(sample_name, &error_msg)) {
175     std::cerr << error_msg;
176     return nullptr;
177   }
178   return samples_.at(sample_name).process_memory;
179 }
180 
GetJitDebug(const std::string & initial_sample_name) const181 JitDebug* OfflineUnwindUtils::GetJitDebug(const std::string& initial_sample_name) const {
182   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
183   std::string error_msg;
184   if (!IsValidUnwindSample(sample_name, &error_msg)) {
185     std::cerr << error_msg;
186     return nullptr;
187   }
188   return samples_.at(sample_name).jit_debug.get();
189 }
190 
GetOfflineFilesPath(const std::string & initial_sample_name) const191 const std::string* OfflineUnwindUtils::GetOfflineFilesPath(
192     const std::string& initial_sample_name) const {
193   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
194   std::string error_msg;
195   if (!IsValidUnwindSample(sample_name, &error_msg)) {
196     std::cerr << error_msg;
197     return nullptr;
198   }
199   return &samples_.at(sample_name).offline_files_path;
200 }
201 
GetFrameInfoFilepath(const std::string & initial_sample_name) const202 const std::string* OfflineUnwindUtils::GetFrameInfoFilepath(
203     const std::string& initial_sample_name) const {
204   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
205   std::string error_msg;
206   if (!IsValidUnwindSample(sample_name, &error_msg)) {
207     std::cerr << error_msg;
208     return nullptr;
209   }
210   return &samples_.at(sample_name).frame_info_filepath;
211 }
212 
Init(const std::vector<UnwindSampleInfo> & sample_infos,std::string * error_msg)213 bool OfflineUnwindUtils::Init(const std::vector<UnwindSampleInfo>& sample_infos,
214                               std::string* error_msg) {
215   // Save the current path so the caller can switch back to it later.
216   cwd_ = std::filesystem::current_path();
217 
218   // Fill in the unwind samples.
219   std::stringstream err_stream;
220   for (const auto& sample_info : sample_infos) {
221     std::string offline_files_full_path =
222         GetOfflineFilesDirectory() + sample_info.offline_files_dir;
223     if (!std::filesystem::exists(offline_files_full_path)) {
224       err_stream << "Offline files directory '" << offline_files_full_path << "' does not exist.";
225       *error_msg = err_stream.str();
226       return false;
227     }
228     std::string frame_info_filepath = offline_files_full_path + sample_info.frame_info_filename;
229 
230     std::string map_buffer;
231     if (!android::base::ReadFileToString((offline_files_full_path + "maps.txt"), &map_buffer)) {
232       err_stream << "Failed to read from '" << offline_files_full_path << "maps.txt' into memory.";
233       *error_msg = err_stream.str();
234       return false;
235     }
236 
237     // CreateMaps, CreatRegs, and Create*Memory may need to be called later by the client. So we
238     // need to create the sample now in case the flags are set to call these methods in Init.
239     const std::string& sample_name = sample_info.offline_files_dir;
240     samples_.emplace(sample_name, (UnwindSample){
241                                       std::move(offline_files_full_path),
242                                       std::move(frame_info_filepath), std::move(map_buffer),
243                                       nullptr,                         // regs
244                                       nullptr,                         // maps
245                                       std::make_shared<MemoryFake>(),  // process_memory
246                                       nullptr,                         // jit_debug
247                                   });
248     UnwindSample& sample = samples_.at(sample_name);
249 
250     if (sample_info.create_maps) {
251       if (!CreateMaps(error_msg, sample_name)) return false;
252     }
253     if (!CreateRegs(sample_info.arch, error_msg, sample_name)) return false;
254 
255     switch (sample_info.memory_flag) {
256       case ProcessMemoryFlag::kNone: {
257         if (!CreateProcessMemory(error_msg, sample_name)) return false;
258         break;
259       }
260       case ProcessMemoryFlag::kIncludeJitMemory: {
261         if (!CreateProcessMemory(error_msg, sample_name)) return false;
262         sample.jit_debug = CreateJitDebug(sample.regs->Arch(), sample.process_memory);
263         break;
264       }
265       case ProcessMemoryFlag::kNoMemory: {
266         break;
267       }
268       default: {
269         std::stringstream err_stream;
270         err_stream << "Unknown memory type for sample '" << sample_name << "'.";
271         *error_msg = err_stream.str();
272         return false;
273       }
274     }
275   }
276   initted_ = true;
277   return true;
278 }
279 
Init(const UnwindSampleInfo & sample_info,std::string * error_msg)280 bool OfflineUnwindUtils::Init(const UnwindSampleInfo& sample_info, std::string* error_msg) {
281   if (Init(std::vector<UnwindSampleInfo>{sample_info}, error_msg)) {
282     if (!ChangeToSampleDirectory(error_msg)) return false;
283     return true;
284   }
285   return false;
286 }
287 
ChangeToSampleDirectory(std::string * error_msg,const std::string & initial_sample_name) const288 bool OfflineUnwindUtils::ChangeToSampleDirectory(std::string* error_msg,
289                                                  const std::string& initial_sample_name) const {
290   if (!initted_) {
291     *error_msg =
292         "Cannot change to sample directory because OfflineUnwindUtils::Init has not been called.";
293     return false;
294   }
295   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
296   if (!IsValidUnwindSample(sample_name, error_msg)) return false;
297 
298   std::filesystem::current_path(std::filesystem::path(samples_.at(sample_name).offline_files_path));
299   return true;
300 }
301 
GetExpectedNumFrames(size_t * expected_num_frames,std::string * error_msg,const std::string & initial_sample_name) const302 bool OfflineUnwindUtils::GetExpectedNumFrames(size_t* expected_num_frames, std::string* error_msg,
303                                               const std::string& initial_sample_name) const {
304   if (!initted_) {
305     *error_msg =
306         "Cannot get expected number of frames of a sample because OfflineUnwindUtils::Init has not "
307         "been called.";
308     return false;
309   }
310   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
311   if (!IsValidUnwindSample(sample_name, error_msg)) return false;
312 
313   const std::string& sample_frames_path = samples_.at(sample_name).frame_info_filepath;
314   if (!std::filesystem::exists(sample_frames_path)) {
315     std::stringstream err_stream;
316     err_stream << "Offline files directory '" << sample_frames_path << "' does not exist.";
317     *error_msg = err_stream.str();
318     return false;
319   }
320 
321   std::ifstream in(sample_frames_path);
322   in.unsetf(std::ios_base::skipws);  // Ensure that we do not skip newlines.
323   *expected_num_frames =
324       std::count(std::istream_iterator<char>(in), std::istream_iterator<char>(), '\n');
325 
326   return true;
327 }
328 
CreateMaps(std::string * error_msg,const std::string & initial_sample_name)329 bool OfflineUnwindUtils::CreateMaps(std::string* error_msg,
330                                     const std::string& initial_sample_name) {
331   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
332   if (!IsValidUnwindSample(sample_name, error_msg)) return false;
333   UnwindSample& sample = samples_.at(sample_name);
334 
335   sample.maps.reset(new BufferMaps(sample.map_buffer.c_str()));
336   if (!sample.maps->Parse()) {
337     *error_msg = "Failed to parse offline maps.";
338     return false;
339   }
340   return true;
341 }
342 
CreateProcessMemory(std::string * error_msg,const std::string & initial_sample_name)343 bool OfflineUnwindUtils::CreateProcessMemory(std::string* error_msg,
344                                              const std::string& initial_sample_name) {
345   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
346   if (!IsValidUnwindSample(sample_name, error_msg)) return false;
347   UnwindSample& sample = samples_.at(sample_name);
348 
349   // Construct process memory from all descriptor, stack, entry, and jit files
350   auto memory = std::make_unique<MemoryOfflineParts>();
351   bool data_files_found = false;
352   for (const auto& file : std::filesystem::directory_iterator(sample.offline_files_path)) {
353     std::string filename = file.path().string();
354     if (std::regex_match(filename,
355                          std::regex("^(.+)\\/(descriptor|stack|entry|jit)(\\d*)\\.data$"))) {
356       data_files_found = true;
357       if (!AddMemory(filename, memory.get(), error_msg)) return false;
358     }
359   }
360   if (!data_files_found) {
361     *error_msg = "No memory (stack, JIT, etc.) data files found.";
362     return false;
363   }
364 
365   sample.process_memory.reset(memory.release());
366   return true;
367 }
368 
369 namespace {
370 template <typename AddressType>
ReadRegs(RegsImpl<AddressType> * regs,const std::unordered_map<std::string,uint32_t> & name_to_reg,std::string * error_msg,const std::string & offline_files_path)371 bool ReadRegs(RegsImpl<AddressType>* regs,
372               const std::unordered_map<std::string, uint32_t>& name_to_reg, std::string* error_msg,
373               const std::string& offline_files_path) {
374   std::stringstream err_stream;
375   FILE* fp = fopen((offline_files_path + "regs.txt").c_str(), "r");
376   if (fp == nullptr) {
377     err_stream << "Error opening file '" << offline_files_path << "regs.txt': " << strerror(errno);
378     *error_msg = err_stream.str();
379     return false;
380   }
381 
382   while (!feof(fp)) {
383     uint64_t value;
384     char reg_name[100];
385     if (fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value) != 2) {
386       err_stream << "Failed to read in register name/values from '" << offline_files_path
387                  << "regs.txt'.";
388       *error_msg = err_stream.str();
389       return false;
390     }
391     std::string name(reg_name);
392     if (!name.empty()) {
393       // Remove the : from the end.
394       name.resize(name.size() - 1);
395     }
396     auto entry = name_to_reg.find(name);
397     if (entry == name_to_reg.end()) {
398       err_stream << "Unknown register named " << reg_name;
399       *error_msg = err_stream.str();
400       return false;
401     }
402     (*regs)[entry->second] = value;
403   }
404   fclose(fp);
405   return true;
406 }
407 }  // namespace
408 
CreateRegs(ArchEnum arch,std::string * error_msg,const std::string & initial_sample_name)409 bool OfflineUnwindUtils::CreateRegs(ArchEnum arch, std::string* error_msg,
410                                     const std::string& initial_sample_name) {
411   const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
412   if (!IsValidUnwindSample(sample_name, error_msg)) return false;
413   auto& regs = samples_.at(sample_name).regs;
414   const auto& offline_files_path = samples_.at(sample_name).offline_files_path;
415 
416   switch (arch) {
417     case ARCH_ARM: {
418       RegsArm* regs_impl = new RegsArm;
419       regs.reset(regs_impl);
420       if (!ReadRegs<uint32_t>(regs_impl, arm_regs_, error_msg, offline_files_path)) return false;
421       break;
422     }
423     case ARCH_ARM64: {
424       RegsArm64* regs_impl = new RegsArm64;
425       regs.reset(regs_impl);
426       if (!ReadRegs<uint64_t>(regs_impl, arm64_regs_, error_msg, offline_files_path)) return false;
427       break;
428     }
429     case ARCH_X86: {
430       RegsX86* regs_impl = new RegsX86;
431       regs.reset(regs_impl);
432       if (!ReadRegs<uint32_t>(regs_impl, x86_regs_, error_msg, offline_files_path)) return false;
433       break;
434     }
435     case ARCH_X86_64: {
436       RegsX86_64* regs_impl = new RegsX86_64;
437       regs.reset(regs_impl);
438       if (!ReadRegs<uint64_t>(regs_impl, x86_64_regs_, error_msg, offline_files_path)) return false;
439       break;
440     }
441     default:
442       *error_msg = "Unknown architechture " + std::to_string(arch);
443       return false;
444   }
445 
446   return true;
447 }
448 
GetAdjustedSampleName(const std::string & initial_sample_name) const449 const std::string& OfflineUnwindUtils::GetAdjustedSampleName(
450     const std::string& initial_sample_name) const {
451   // Only return the first entry in the sample map if this is the single unwind use case.
452   // Otherwise return the inputted sample name so we can check if that is a valid sample name.
453   if (initial_sample_name == kSingleSample && samples_.size() == 1) {
454     return samples_.begin()->first;
455   }
456   return initial_sample_name;
457 }
458 
IsValidUnwindSample(const std::string & sample_name,std::string * error_msg) const459 bool OfflineUnwindUtils::IsValidUnwindSample(const std::string& sample_name,
460                                              std::string* error_msg) const {
461   if (samples_.find(sample_name) == samples_.end()) {
462     std::stringstream err_stream;
463     err_stream << "Invalid sample name (offline file directory) '" << sample_name << "'.";
464     if (sample_name == kSingleSample) {
465       err_stream << " An explicit sample name must be provided for the multiple unwind use case "
466                     "of OfflineUnwindUtils (i.e. should not use the default sample name).";
467     }
468     *error_msg = err_stream.str();
469     return false;
470   }
471   return true;
472 }
473 
474 std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm_regs_ = {
475     {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
476     {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
477     {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
478     {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
479 };
480 
481 std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm64_regs_ = {
482     {"x0", ARM64_REG_R0},      {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},
483     {"x3", ARM64_REG_R3},      {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},
484     {"x6", ARM64_REG_R6},      {"x7", ARM64_REG_R7},   {"x8", ARM64_REG_R8},
485     {"x9", ARM64_REG_R9},      {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
486     {"x12", ARM64_REG_R12},    {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
487     {"x15", ARM64_REG_R15},    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
488     {"x18", ARM64_REG_R18},    {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
489     {"x21", ARM64_REG_R21},    {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
490     {"x24", ARM64_REG_R24},    {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
491     {"x27", ARM64_REG_R27},    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
492     {"sp", ARM64_REG_SP},      {"lr", ARM64_REG_LR},   {"pc", ARM64_REG_PC},
493     {"pst", ARM64_REG_PSTATE},
494 };
495 
496 std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_regs_ = {
497     {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
498     {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
499     {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
500 };
501 
502 std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_64_regs_ = {
503     {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
504     {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
505     {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
506     {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
507     {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
508     {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
509 };
510 
511 }  // namespace unwindstack
512