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