1 /*
2 * Copyright (C) 2020 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 "MapRecordReader.h"
18
19 #include <stdint.h>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22
23 #include <vector>
24
25 #include <android-base/strings.h>
26
27 #include "environment.h"
28 #include "thread_tree.h"
29
30 namespace simpleperf {
31
ReadKernelMaps()32 bool MapRecordReader::ReadKernelMaps() {
33 KernelMmap kernel_mmap;
34 std::vector<KernelMmap> module_mmaps;
35 GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
36
37 // Inject an mmap record covering all of kernel's address space. This is used to set up a map for
38 // dynamically allocated BPF JIT regions outside [kernel.kallsyms]. Needs to be added first.
39 MmapRecord bpf_record(attr_, true, UINT_MAX, 0, 0, std::numeric_limits<uint64_t>::max(), 0,
40 DEFAULT_KERNEL_BPF_MMAP_NAME, event_id_);
41 if (!callback_(&bpf_record)) {
42 return false;
43 }
44 MmapRecord mmap_record(attr_, true, UINT_MAX, 0, kernel_mmap.start_addr, kernel_mmap.len, 0,
45 kernel_mmap.filepath, event_id_);
46 if (!callback_(&mmap_record)) {
47 return false;
48 }
49 for (const auto& module_mmap : module_mmaps) {
50 MmapRecord mmap_record(attr_, true, UINT_MAX, 0, module_mmap.start_addr, module_mmap.len, 0,
51 module_mmap.filepath, event_id_);
52 if (!callback_(&mmap_record)) {
53 return false;
54 }
55 }
56 return true;
57 }
58
ReadProcessMaps(pid_t pid,uint64_t timestamp)59 bool MapRecordReader::ReadProcessMaps(pid_t pid, uint64_t timestamp) {
60 std::vector<pid_t> tids = GetThreadsInProcess(pid);
61 return ReadProcessMaps(pid, std::unordered_set<pid_t>(tids.begin(), tids.end()), timestamp);
62 }
63
ReadProcessMaps(pid_t pid,const std::unordered_set<pid_t> & tids,uint64_t timestamp)64 bool MapRecordReader::ReadProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids,
65 uint64_t timestamp) {
66 // Dump mmap records.
67 std::vector<ThreadMmap> thread_mmaps;
68 if (!GetThreadMmapsInProcess(pid, &thread_mmaps)) {
69 // The process may exit before we get its info.
70 return true;
71 }
72 for (const auto& map : thread_mmaps) {
73 if (!(map.prot & PROT_EXEC) && !keep_non_executable_maps_) {
74 continue;
75 }
76 Mmap2Record record(attr_, false, pid, pid, map.start_addr, map.len, map.pgoff, map.prot,
77 map.name, event_id_, timestamp);
78 if (!callback_(&record)) {
79 return false;
80 }
81 }
82 // Dump process name.
83 std::string process_name = GetCompleteProcessName(pid);
84 if (!process_name.empty()) {
85 CommRecord record(attr_, pid, pid, process_name, event_id_, timestamp);
86 if (!callback_(&record)) {
87 return false;
88 }
89 }
90 // Dump thread info.
91 for (const auto& tid : tids) {
92 std::string name;
93 if (tid != pid && GetThreadName(tid, &name)) {
94 // If a thread name matches the suffix of its process name, probably the thread name
95 // is stripped by TASK_COMM_LEN.
96 if (android::base::EndsWith(process_name, name)) {
97 name = process_name;
98 }
99 CommRecord comm_record(attr_, pid, tid, name, event_id_, timestamp);
100 if (!callback_(&comm_record)) {
101 return false;
102 }
103 }
104 }
105 return true;
106 }
107
MapRecordThread(const MapRecordReader & map_record_reader)108 MapRecordThread::MapRecordThread(const MapRecordReader& map_record_reader)
109 : map_record_reader_(map_record_reader), fp_(nullptr, fclose) {
110 map_record_reader_.SetCallback([this](Record* r) { return WriteRecordToFile(r); });
111 tmpfile_ = ScopedTempFiles::CreateTempFile();
112 fp_.reset(fdopen(tmpfile_->release(), "r+"));
113 thread_ = std::thread([this]() { thread_result_ = RunThread(); });
114 }
115
~MapRecordThread()116 MapRecordThread::~MapRecordThread() {
117 if (thread_.joinable()) {
118 early_stop_ = true;
119 thread_.join();
120 }
121 }
122
RunThread()123 bool MapRecordThread::RunThread() {
124 if (!fp_) {
125 return false;
126 }
127 if (!map_record_reader_.ReadKernelMaps()) {
128 return false;
129 }
130 for (auto pid : GetAllProcesses()) {
131 if (early_stop_) {
132 return false;
133 }
134 if (!map_record_reader_.ReadProcessMaps(pid, 0)) {
135 return false;
136 }
137 }
138 return true;
139 }
140
WriteRecordToFile(Record * record)141 bool MapRecordThread::WriteRecordToFile(Record* record) {
142 if (fwrite(record->Binary(), record->size(), 1, fp_.get()) != 1) {
143 PLOG(ERROR) << "failed to write map records to file";
144 return false;
145 }
146 return true;
147 }
148
Join()149 bool MapRecordThread::Join() {
150 if (!thread_joined_) {
151 thread_.join();
152 thread_joined_ = true;
153 if (!thread_result_) {
154 LOG(ERROR) << "map record thread failed";
155 }
156 }
157 return thread_result_;
158 }
159
ReadMapRecordData(const std::function<bool (const char *,size_t)> & callback)160 bool MapRecordThread::ReadMapRecordData(const std::function<bool(const char*, size_t)>& callback) {
161 if (fseek(fp_.get(), 0, SEEK_END) != 0) {
162 PLOG(ERROR) << "fseek() failed";
163 return false;
164 }
165 off_t offset = ftello(fp_.get());
166 if (offset == -1) {
167 PLOG(ERROR) << "ftello() failed";
168 return false;
169 }
170 uint64_t file_size = static_cast<uint64_t>(offset);
171 if (fseek(fp_.get(), 0, SEEK_SET) != 0) {
172 PLOG(ERROR) << "fseek() failed";
173 return false;
174 }
175 std::vector<char> buffer(1024 * 1024);
176 uint64_t left_bytes = file_size;
177 while (left_bytes > 0) {
178 size_t to_read = left_bytes > buffer.size() ? buffer.size() : left_bytes;
179 if (fread(buffer.data(), to_read, 1, fp_.get()) != 1) {
180 PLOG(ERROR) << "fread() failed";
181 return false;
182 }
183 if (!callback(buffer.data(), to_read)) {
184 return false;
185 }
186 left_bytes -= to_read;
187 }
188 return true;
189 }
190
ReadMapRecords(const std::function<void (const Record *)> & callback,bool only_kernel_maps)191 bool MapRecordThread::ReadMapRecords(const std::function<void(const Record*)>& callback,
192 bool only_kernel_maps) {
193 if (fseek(fp_.get(), 0, SEEK_END) != 0) {
194 PLOG(ERROR) << "fseek() failed";
195 return false;
196 }
197 off_t offset = ftello(fp_.get());
198 if (offset == -1) {
199 PLOG(ERROR) << "ftello() failed";
200 return false;
201 }
202 uint64_t file_size = static_cast<uint64_t>(offset);
203 if (fseek(fp_.get(), 0, SEEK_SET) != 0) {
204 PLOG(ERROR) << "fseek() failed";
205 return false;
206 }
207 uint64_t nread = 0;
208 std::vector<char> buffer(1024);
209 while (nread < file_size) {
210 if (fread(buffer.data(), Record::header_size(), 1, fp_.get()) != 1) {
211 PLOG(ERROR) << "fread() failed";
212 return false;
213 }
214 RecordHeader header;
215 if (!header.Parse(buffer.data())) {
216 return false;
217 }
218 if (buffer.size() < header.size) {
219 buffer.resize(header.size);
220 }
221 if (fread(buffer.data() + Record::header_size(), header.size - Record::header_size(), 1,
222 fp_.get()) != 1) {
223 PLOG(ERROR) << "fread() failed";
224 return false;
225 }
226 auto r = ReadRecordFromBuffer(map_record_reader_.Attr(), header.type, buffer.data(),
227 buffer.data() + header.size);
228 CHECK(r);
229 if (only_kernel_maps && !r->InKernel()) {
230 break;
231 }
232 callback(r.get());
233 nread += header.size;
234 }
235 return true;
236 }
237
238 } // namespace simpleperf
239