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