1 /* 2 * Copyright (C) 2019 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 <stdint.h> 20 #include <sys/mman.h> 21 #include <sys/types.h> 22 #include <sys/wait.h> 23 #include <unistd.h> 24 25 #include <string> 26 27 #include <android-base/file.h> 28 #include <android-base/strings.h> 29 #include <ziparchive/zip_archive.h> 30 31 #include "Alloc.h" 32 #include "File.h" 33 34 std::string ZipGetContents(const char* filename) { 35 ZipArchiveHandle archive; 36 if (OpenArchive(filename, &archive) != 0) { 37 return ""; 38 } 39 40 // It is assumed that the archive contains only a single entry. 41 void* cookie; 42 std::string contents; 43 if (StartIteration(archive, &cookie) == 0) { 44 ZipEntry entry; 45 std::string name; 46 if (Next(cookie, &entry, &name) == 0) { 47 contents.resize(entry.uncompressed_length); 48 if (ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(contents.data()), 49 entry.uncompressed_length) != 0) { 50 contents = ""; 51 } 52 } 53 } 54 55 CloseArchive(archive); 56 return contents; 57 } 58 59 static void WaitPid(pid_t pid) { 60 int wstatus; 61 pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, &wstatus, 0)); 62 if (wait_pid != pid) { 63 if (wait_pid == -1) { 64 err(1, "waitpid() failed"); 65 } else { 66 errx(1, "Unexpected pid from waitpid(): expected %d, returned %d", pid, wait_pid); 67 } 68 } 69 if (!WIFEXITED(wstatus)) { 70 errx(1, "Forked process did not terminate with exit() call"); 71 } 72 if (WEXITSTATUS(wstatus) != 0) { 73 errx(1, "Bad exit value from forked process: returned %d", WEXITSTATUS(wstatus)); 74 } 75 } 76 77 // This function should not do any memory allocations in the main function. 78 // Any true allocation should happen in fork'd code. 79 void GetUnwindInfo(const char* filename, AllocEntry** entries, size_t* num_entries) { 80 void* mem = 81 mmap(nullptr, sizeof(size_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); 82 if (mem == MAP_FAILED) { 83 err(1, "Unable to allocate a shared map of size %zu", sizeof(size_t)); 84 } 85 *reinterpret_cast<size_t*>(mem) = 0; 86 87 pid_t pid; 88 if ((pid = fork()) == 0) { 89 // First get the number of lines in the trace file. It is assumed 90 // that there are no blank lines, and every line contains a valid 91 // allocation operation. 92 std::string contents; 93 if (android::base::EndsWith(filename, ".zip")) { 94 contents = ZipGetContents(filename); 95 } else if (!android::base::ReadFileToString(filename, &contents)) { 96 errx(1, "Unable to get contents of %s", filename); 97 } 98 if (contents.empty()) { 99 errx(1, "Unable to get contents of %s", filename); 100 } 101 102 size_t lines = 0; 103 size_t index = 0; 104 while (true) { 105 index = contents.find('\n', index); 106 if (index == std::string::npos) { 107 break; 108 } 109 index++; 110 lines++; 111 } 112 if (contents[contents.size() - 1] != '\n') { 113 // Add one since the last line doesn't end in '\n'. 114 lines++; 115 } 116 *reinterpret_cast<size_t*>(mem) = lines; 117 _exit(0); 118 } else if (pid == -1) { 119 err(1, "fork() call failed"); 120 } 121 WaitPid(pid); 122 *num_entries = *reinterpret_cast<size_t*>(mem); 123 munmap(mem, sizeof(size_t)); 124 125 mem = mmap(nullptr, *num_entries * sizeof(AllocEntry), PROT_READ | PROT_WRITE, 126 MAP_ANONYMOUS | MAP_SHARED, -1, 0); 127 if (mem == MAP_FAILED) { 128 err(1, "Unable to allocate a shared map of size %zu", *num_entries * sizeof(AllocEntry)); 129 } 130 *entries = reinterpret_cast<AllocEntry*>(mem); 131 132 if ((pid = fork()) == 0) { 133 std::string contents; 134 if (android::base::EndsWith(filename, ".zip")) { 135 contents = ZipGetContents(filename); 136 } else if (!android::base::ReadFileToString(filename, &contents)) { 137 errx(1, "Unable to get contents of %s", filename); 138 } 139 if (contents.empty()) { 140 errx(1, "Contents of zip file %s is empty.", filename); 141 } 142 143 size_t entry_idx = 0; 144 size_t start_str = 0; 145 size_t end_str = 0; 146 while (true) { 147 end_str = contents.find('\n', start_str); 148 if (end_str == std::string::npos) { 149 break; 150 } 151 if (entry_idx == *num_entries) { 152 errx(1, "Too many entries, stopped at entry %zu", entry_idx); 153 } 154 contents[end_str] = '\0'; 155 AllocGetData(&contents[start_str], &(*entries)[entry_idx++]); 156 start_str = end_str + 1; 157 } 158 if (entry_idx != *num_entries) { 159 errx(1, "Mismatched number of entries found: expected %zu, found %zu", *num_entries, 160 entry_idx); 161 } 162 _exit(0); 163 } else if (pid == -1) { 164 err(1, "fork() call failed"); 165 } 166 WaitPid(pid); 167 } 168 169 void FreeEntries(AllocEntry* entries, size_t num_entries) { 170 munmap(entries, num_entries * sizeof(AllocEntry)); 171 } 172