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
29 namespace simpleperf {
30
ReadKernelMaps()31 bool MapRecordReader::ReadKernelMaps() {
32 KernelMmap kernel_mmap;
33 std::vector<KernelMmap> module_mmaps;
34 GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
35
36 MmapRecord mmap_record(attr_, true, UINT_MAX, 0, kernel_mmap.start_addr, kernel_mmap.len, 0,
37 kernel_mmap.filepath, event_id_);
38 if (!callback_(&mmap_record)) {
39 return false;
40 }
41 for (const auto& module_mmap : module_mmaps) {
42 MmapRecord mmap_record(attr_, true, UINT_MAX, 0, module_mmap.start_addr, module_mmap.len, 0,
43 module_mmap.filepath, event_id_);
44 if (!callback_(&mmap_record)) {
45 return false;
46 }
47 }
48 return true;
49 }
50
ReadProcessMaps(pid_t pid,uint64_t timestamp)51 bool MapRecordReader::ReadProcessMaps(pid_t pid, uint64_t timestamp) {
52 std::vector<pid_t> tids = GetThreadsInProcess(pid);
53 return ReadProcessMaps(pid, std::unordered_set<pid_t>(tids.begin(), tids.end()), timestamp);
54 }
55
ReadProcessMaps(pid_t pid,const std::unordered_set<pid_t> & tids,uint64_t timestamp)56 bool MapRecordReader::ReadProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids,
57 uint64_t timestamp) {
58 // Dump mmap records.
59 std::vector<ThreadMmap> thread_mmaps;
60 if (!GetThreadMmapsInProcess(pid, &thread_mmaps)) {
61 // The process may exit before we get its info.
62 return true;
63 }
64 for (const auto& map : thread_mmaps) {
65 if (!(map.prot & PROT_EXEC) && !keep_non_executable_maps_) {
66 continue;
67 }
68 Mmap2Record record(attr_, false, pid, pid, map.start_addr, map.len, map.pgoff, map.prot,
69 map.name, event_id_, timestamp);
70 if (!callback_(&record)) {
71 return false;
72 }
73 }
74 // Dump process name.
75 std::string process_name = GetCompleteProcessName(pid);
76 if (!process_name.empty()) {
77 CommRecord record(attr_, pid, pid, process_name, event_id_, timestamp);
78 if (!callback_(&record)) {
79 return false;
80 }
81 }
82 // Dump thread info.
83 for (const auto& tid : tids) {
84 std::string name;
85 if (tid != pid && GetThreadName(tid, &name)) {
86 // If a thread name matches the suffix of its process name, probably the thread name
87 // is stripped by TASK_COMM_LEN.
88 if (android::base::EndsWith(process_name, name)) {
89 name = process_name;
90 }
91 CommRecord comm_record(attr_, pid, tid, name, event_id_, timestamp);
92 if (!callback_(&comm_record)) {
93 return false;
94 }
95 }
96 }
97 return true;
98 }
99
MapRecordThread(const MapRecordReader & map_record_reader)100 MapRecordThread::MapRecordThread(const MapRecordReader& map_record_reader)
101 : map_record_reader_(map_record_reader), fp_(nullptr, fclose) {
102 map_record_reader_.SetCallback([this](Record* r) { return WriteRecordToFile(r); });
103 tmpfile_ = ScopedTempFiles::CreateTempFile();
104 fp_.reset(fdopen(tmpfile_->release(), "r+"));
105 thread_ = std::thread([this]() { thread_result_ = RunThread(); });
106 }
107
~MapRecordThread()108 MapRecordThread::~MapRecordThread() {
109 if (thread_.joinable()) {
110 early_stop_ = true;
111 thread_.join();
112 }
113 }
114
RunThread()115 bool MapRecordThread::RunThread() {
116 if (!fp_) {
117 return false;
118 }
119 if (!map_record_reader_.ReadKernelMaps()) {
120 return false;
121 }
122 for (auto pid : GetAllProcesses()) {
123 if (early_stop_) {
124 return false;
125 }
126 if (!map_record_reader_.ReadProcessMaps(pid, 0)) {
127 return false;
128 }
129 }
130 return true;
131 }
132
WriteRecordToFile(Record * record)133 bool MapRecordThread::WriteRecordToFile(Record* record) {
134 if (fwrite(record->Binary(), record->size(), 1, fp_.get()) != 1) {
135 PLOG(ERROR) << "failed to write map records to file";
136 return false;
137 }
138 return true;
139 }
140
Join()141 bool MapRecordThread::Join() {
142 thread_.join();
143 if (!thread_result_) {
144 LOG(ERROR) << "map record thread failed";
145 }
146 return thread_result_;
147 }
148
ReadMapRecords(const std::function<bool (Record *)> & callback)149 bool MapRecordThread::ReadMapRecords(const std::function<bool(Record*)>& callback) {
150 off_t offset = ftello(fp_.get());
151 if (offset == -1) {
152 PLOG(ERROR) << "ftello() failed";
153 return false;
154 }
155 uint64_t file_size = static_cast<uint64_t>(offset);
156 if (fseek(fp_.get(), 0, SEEK_SET) != 0) {
157 PLOG(ERROR) << "fseek() failed";
158 return false;
159 }
160 uint64_t nread = 0;
161 std::vector<char> buffer(1024);
162 while (nread < file_size) {
163 if (fread(buffer.data(), Record::header_size(), 1, fp_.get()) != 1) {
164 PLOG(ERROR) << "fread() failed";
165 return false;
166 }
167 RecordHeader header(buffer.data());
168 if (buffer.size() < header.size) {
169 buffer.resize(header.size);
170 }
171 if (fread(buffer.data() + Record::header_size(), header.size - Record::header_size(), 1,
172 fp_.get()) != 1) {
173 PLOG(ERROR) << "fread() failed";
174 return false;
175 }
176 auto r = ReadRecordFromBuffer(map_record_reader_.Attr(), header.type, buffer.data());
177 CHECK(r);
178 if (!callback(r.get())) {
179 return false;
180 }
181 nread += header.size;
182 }
183 return true;
184 }
185
186 } // namespace simpleperf
187