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 
ZipGetContents(const char * filename)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 
WaitPid(pid_t pid)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.
GetUnwindInfo(const char * filename,AllocEntry ** entries,size_t * num_entries)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 
FreeEntries(AllocEntry * entries,size_t num_entries)169 void FreeEntries(AllocEntry* entries, size_t num_entries) {
170   munmap(entries, num_entries * sizeof(AllocEntry));
171 }
172