blob: d6fa6b54918b072cfbee9060c31f6f394d770da3 [file] [log] [blame]
Anna Zappone2a6f9042018-03-14 13:26:07 +00001/*
2 * Copyright (C) 2018 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 "src/traced/probes/filesystem/inode_file_data_source.h"
18
19#include <dirent.h>
Anna Zappone80e098e2018-03-16 14:50:28 +000020#include <sys/stat.h>
Anna Zappone2a6f9042018-03-14 13:26:07 +000021#include <sys/types.h>
22#include <unistd.h>
23#include <queue>
Anna Zappone9ab79602018-03-27 11:57:44 +010024#include <unordered_map>
Anna Zappone2a6f9042018-03-14 13:26:07 +000025
Anna Zappone2a6f9042018-03-14 13:26:07 +000026#include "perfetto/base/logging.h"
Florian Mayerfe28afd2018-03-22 16:24:10 +000027#include "perfetto/base/scoped_file.h"
Anna Zappone2a6f9042018-03-14 13:26:07 +000028#include "perfetto/tracing/core/trace_packet.h"
Primiano Tucci569ad312018-03-14 22:28:31 +000029#include "perfetto/tracing/core/trace_writer.h"
Anna Zappone2a6f9042018-03-14 13:26:07 +000030
31#include "perfetto/trace/trace_packet.pbzero.h"
Florian Mayer6f9c3422018-03-29 10:37:41 +010032#include "src/traced/probes/filesystem/file_scanner.h"
Anna Zappone2a6f9042018-03-14 13:26:07 +000033
34namespace perfetto {
Florian Mayer112376e2018-03-28 16:20:41 +010035namespace {
Primiano Tuccidae35652018-03-29 18:32:02 +010036constexpr uint64_t kScanIntervalMs = 10000; // 10s
37constexpr uint64_t kScanDelayMs = 10000; // 10s
38constexpr uint64_t kScanBatchSize = 15000;
39
40uint64_t OrDefault(uint64_t value, uint64_t def) {
41 if (value != 0)
42 return value;
43 return def;
44}
Florian Mayer112376e2018-03-28 16:20:41 +010045
Florian Mayera5dd0b82018-03-29 12:31:00 +010046class StaticMapDelegate : public FileScanner::Delegate {
47 public:
48 StaticMapDelegate(
49 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map)
50 : map_(map) {}
51 ~StaticMapDelegate() {}
Florian Mayerde42bd82018-03-27 17:52:23 +010052
Florian Mayera5dd0b82018-03-29 12:31:00 +010053 private:
54 bool OnInodeFound(BlockDeviceID block_device_id,
55 Inode inode_number,
56 const std::string& path,
57 protos::pbzero::InodeFileMap_Entry_Type type) {
58 std::unordered_map<Inode, InodeMapValue>& inode_map =
59 (*map_)[block_device_id];
60 inode_map[inode_number].SetType(type);
61 inode_map[inode_number].AddPath(path);
62 return true;
Anna Zappone2a6f9042018-03-14 13:26:07 +000063 }
Florian Mayera5dd0b82018-03-29 12:31:00 +010064 void OnInodeScanDone() {}
65 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map_;
66};
Anna Zappone2a6f9042018-03-14 13:26:07 +000067}
68
Anna Zappone3be7b672018-03-23 17:26:10 +000069void CreateStaticDeviceToInodeMap(
Florian Mayer7ca14bf2018-03-20 14:13:02 +000070 const std::string& root_directory,
Anna Zappone9ab79602018-03-27 11:57:44 +010071 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
72 static_file_map) {
Florian Mayera5dd0b82018-03-29 12:31:00 +010073 StaticMapDelegate delegate(static_file_map);
74 FileScanner scanner({root_directory}, &delegate);
75 scanner.Scan();
Anna Zappone3be7b672018-03-23 17:26:10 +000076}
77
78void FillInodeEntry(InodeFileMap* destination,
79 Inode inode_number,
80 const InodeMapValue& inode_map_value) {
81 auto* entry = destination->add_entries();
82 entry->set_inode_number(inode_number);
83 entry->set_type(inode_map_value.type());
84 for (const auto& path : inode_map_value.paths())
85 entry->add_paths(path.c_str());
Florian Mayer7ca14bf2018-03-20 14:13:02 +000086}
87
Anna Zappone2a6f9042018-03-14 13:26:07 +000088InodeFileDataSource::InodeFileDataSource(
Primiano Tuccidae35652018-03-29 18:32:02 +010089 DataSourceConfig source_config,
Florian Mayer112376e2018-03-28 16:20:41 +010090 base::TaskRunner* task_runner,
Hector Dearman0ff07c72018-03-15 09:54:46 +000091 TracingSessionID id,
Anna Zappone9ab79602018-03-27 11:57:44 +010092 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
93 static_file_map,
Anna Zappone3be7b672018-03-23 17:26:10 +000094 LRUInodeCache* cache,
Anna Zappone2a6f9042018-03-14 13:26:07 +000095 std::unique_ptr<TraceWriter> writer)
Primiano Tuccidae35652018-03-29 18:32:02 +010096 : source_config_(std::move(source_config)),
97 task_runner_(task_runner),
Florian Mayer112376e2018-03-28 16:20:41 +010098 session_id_(id),
Anna Zappone3be7b672018-03-23 17:26:10 +000099 static_file_map_(static_file_map),
100 cache_(cache),
Hector Dearman0ff07c72018-03-15 09:54:46 +0000101 writer_(std::move(writer)),
102 weak_factory_(this) {}
Anna Zappone2a6f9042018-03-14 13:26:07 +0000103
Florian Mayer20c9ac72018-03-29 12:20:48 +0100104void InodeFileDataSource::AddInodesFromStaticMap(
105 BlockDeviceID block_device_id,
106 std::set<Inode>* inode_numbers) {
Anna Zappone3be7b672018-03-23 17:26:10 +0000107 // Check if block device id exists in static file map
108 auto static_map_entry = static_file_map_->find(block_device_id);
109 if (static_map_entry == static_file_map_->end())
110 return;
111
112 uint64_t system_found_count = 0;
113 for (auto it = inode_numbers->begin(); it != inode_numbers->end();) {
114 Inode inode_number = *it;
115 // Check if inode number exists in static file map for given block device id
116 auto inode_it = static_map_entry->second.find(inode_number);
117 if (inode_it == static_map_entry->second.end()) {
118 ++it;
119 continue;
120 }
121 system_found_count++;
122 it = inode_numbers->erase(it);
Florian Mayer20c9ac72018-03-29 12:20:48 +0100123 FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
124 inode_it->second);
Anna Zappone3be7b672018-03-23 17:26:10 +0000125 }
126 PERFETTO_DLOG("%" PRIu64 " inodes found in static file map",
127 system_found_count);
128}
129
Florian Mayer20c9ac72018-03-29 12:20:48 +0100130void InodeFileDataSource::AddInodesFromLRUCache(
131 BlockDeviceID block_device_id,
132 std::set<Inode>* inode_numbers) {
Anna Zappone3be7b672018-03-23 17:26:10 +0000133 uint64_t cache_found_count = 0;
134 for (auto it = inode_numbers->begin(); it != inode_numbers->end();) {
135 Inode inode_number = *it;
136 auto value = cache_->Get(std::make_pair(block_device_id, inode_number));
137 if (value == nullptr) {
138 ++it;
139 continue;
140 }
141 cache_found_count++;
142 it = inode_numbers->erase(it);
Florian Mayer20c9ac72018-03-29 12:20:48 +0100143 FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
144 *value);
Anna Zappone3be7b672018-03-23 17:26:10 +0000145 }
Anna Zappone9ab79602018-03-27 11:57:44 +0100146 if (cache_found_count > 0)
147 PERFETTO_DLOG("%" PRIu64 " inodes found in cache", cache_found_count);
Anna Zappone80e098e2018-03-16 14:50:28 +0000148}
149
150void InodeFileDataSource::OnInodes(
151 const std::vector<std::pair<Inode, BlockDeviceID>>& inodes) {
Anna Zappone2a6f9042018-03-14 13:26:07 +0000152 if (mount_points_.empty()) {
153 mount_points_ = ParseMounts();
154 }
155 // Group inodes from FtraceMetadata by block device
Anna Zappone2a6f9042018-03-14 13:26:07 +0000156 std::map<BlockDeviceID, std::set<Inode>> inode_file_maps;
Anna Zappone80e098e2018-03-16 14:50:28 +0000157 for (const auto& inodes_pair : inodes) {
158 Inode inode_number = inodes_pair.first;
159 BlockDeviceID block_device_id = inodes_pair.second;
Anna Zappone2a6f9042018-03-14 13:26:07 +0000160 inode_file_maps[block_device_id].emplace(inode_number);
161 }
Hector Dearman7fabd702018-03-28 12:37:15 +0100162 if (inode_file_maps.size() > 1)
163 PERFETTO_DLOG("Saw %zu block devices.", inode_file_maps.size());
Anna Zappone3be7b672018-03-23 17:26:10 +0000164
Anna Zappone2a6f9042018-03-14 13:26:07 +0000165 // Write a TracePacket with an InodeFileMap proto for each block device id
Florian Mayer112376e2018-03-28 16:20:41 +0100166 for (auto& inode_file_map_data : inode_file_maps) {
Anna Zappone80e098e2018-03-16 14:50:28 +0000167 BlockDeviceID block_device_id = inode_file_map_data.first;
Florian Mayer112376e2018-03-28 16:20:41 +0100168 std::set<Inode>& inode_numbers = inode_file_map_data.second;
Anna Zappone3be7b672018-03-23 17:26:10 +0000169 PERFETTO_DLOG("Saw %zu unique inode numbers.", inode_numbers.size());
Anna Zappone80e098e2018-03-16 14:50:28 +0000170
Anna Zappone3be7b672018-03-23 17:26:10 +0000171 // Add entries to InodeFileMap as inodes are found and resolved to their
172 // paths/type
Florian Mayer20c9ac72018-03-29 12:20:48 +0100173 AddInodesFromStaticMap(block_device_id, &inode_numbers);
174 AddInodesFromLRUCache(block_device_id, &inode_numbers);
Primiano Tuccidae35652018-03-29 18:32:02 +0100175
176 if (source_config_.inode_file_config().do_not_scan())
177 inode_numbers.clear();
178
Florian Mayer112376e2018-03-28 16:20:41 +0100179 if (!inode_numbers.empty()) {
Florian Mayer20c9ac72018-03-29 12:20:48 +0100180 // Try to piggy back the current scan.
181 auto it = missing_inodes_.find(block_device_id);
182 if (it != missing_inodes_.end()) {
183 it->second.insert(inode_numbers.cbegin(), inode_numbers.cend());
184 }
185 next_missing_inodes_[block_device_id].insert(inode_numbers.cbegin(),
186 inode_numbers.cend());
187 if (!scan_running_) {
188 scan_running_ = true;
Florian Mayer112376e2018-03-28 16:20:41 +0100189 auto weak_this = GetWeakPtr();
190 task_runner_->PostDelayedTask(
191 [weak_this] {
192 if (!weak_this) {
193 PERFETTO_DLOG("Giving up filesystem scan.");
194 return;
195 }
196 weak_this.get()->FindMissingInodes();
197 },
Primiano Tuccidae35652018-03-29 18:32:02 +0100198 GetScanDelayMs());
Florian Mayer112376e2018-03-28 16:20:41 +0100199 }
200 }
Anna Zappone2a6f9042018-03-14 13:26:07 +0000201 }
202}
203
Florian Mayer20c9ac72018-03-29 12:20:48 +0100204InodeFileMap* InodeFileDataSource::AddToCurrentTracePacket(
205 BlockDeviceID block_device_id) {
206 if (!has_current_trace_packet_ ||
207 current_block_device_id_ != block_device_id) {
208 if (has_current_trace_packet_)
209 current_trace_packet_->Finalize();
210 current_trace_packet_ = writer_->NewTracePacket();
211 current_file_map_ = current_trace_packet_->set_inode_file_map();
212 has_current_trace_packet_ = true;
Florian Mayer112376e2018-03-28 16:20:41 +0100213
Florian Mayer112376e2018-03-28 16:20:41 +0100214 // Add block device id to InodeFileMap
Florian Mayer20c9ac72018-03-29 12:20:48 +0100215 current_file_map_->set_block_device_id(block_device_id);
216 // Add mount points to InodeFileMap
217 auto range = mount_points_.equal_range(block_device_id);
218 for (std::multimap<BlockDeviceID, std::string>::iterator it = range.first;
219 it != range.second; ++it)
220 current_file_map_->add_mount_points(it->second.c_str());
Florian Mayer112376e2018-03-28 16:20:41 +0100221 }
Florian Mayer20c9ac72018-03-29 12:20:48 +0100222 return current_file_map_;
223}
224
225bool InodeFileDataSource::OnInodeFound(
226 BlockDeviceID block_device_id,
227 Inode inode_number,
228 const std::string& path,
229 protos::pbzero::InodeFileMap_Entry_Type type) {
230 PERFETTO_DLOG("Saw %s %lu", path.c_str(), block_device_id);
231 auto it = missing_inodes_.find(block_device_id);
232 if (it == missing_inodes_.end())
233 return true;
234
235 PERFETTO_DLOG("Missing %lu / %lu", missing_inodes_.size(), it->second.size());
236 size_t n = it->second.erase(inode_number);
237 if (n == 0)
238 return true;
239
240 if (it->second.empty())
241 missing_inodes_.erase(it);
242
243 std::pair<BlockDeviceID, Inode> key{block_device_id, inode_number};
244 auto cur_val = cache_->Get(key);
245 if (cur_val) {
246 cur_val->AddPath(path);
247 FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
248 *cur_val);
249 } else {
250 InodeMapValue new_val(InodeMapValue(type, {path}));
251 cache_->Insert(key, new_val);
252 FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
253 new_val);
254 }
255 PERFETTO_DLOG("Filled %s", path.c_str());
256 return !missing_inodes_.empty();
257}
258
259void InodeFileDataSource::OnInodeScanDone() {
260 // Finalize the accumulated trace packets.
261 current_block_device_id_ = 0;
262 current_file_map_ = nullptr;
263 if (has_current_trace_packet_)
264 current_trace_packet_->Finalize();
265 has_current_trace_packet_ = false;
266 file_scanner_.reset();
267 if (next_missing_inodes_.empty()) {
268 scan_running_ = false;
269 } else {
270 auto weak_this = GetWeakPtr();
271 PERFETTO_DLOG("Starting another filesystem scan.");
272 task_runner_->PostDelayedTask(
273 [weak_this] {
274 if (!weak_this) {
275 PERFETTO_DLOG("Giving up filesystem scan.");
276 return;
277 }
278 weak_this->FindMissingInodes();
279 },
Primiano Tuccidae35652018-03-29 18:32:02 +0100280 GetScanDelayMs());
Florian Mayer20c9ac72018-03-29 12:20:48 +0100281 }
282}
283
284void InodeFileDataSource::AddRootsForBlockDevice(
285 BlockDeviceID block_device_id,
286 std::vector<std::string>* roots) {
287 auto p = mount_points_.equal_range(block_device_id);
288 for (auto it = p.first; it != p.second; ++it)
289 roots->emplace_back(it->second);
290}
291
292void InodeFileDataSource::FindMissingInodes() {
293 missing_inodes_ = std::move(next_missing_inodes_);
294 std::vector<std::string> roots;
295 for (auto& p : missing_inodes_)
296 AddRootsForBlockDevice(p.first, &roots);
297
298 PERFETTO_DCHECK(file_scanner_.get() == nullptr);
299 auto weak_this = GetWeakPtr();
Primiano Tuccidae35652018-03-29 18:32:02 +0100300 file_scanner_ = std::unique_ptr<FileScanner>(new FileScanner(
301 std::move(roots), this, GetScanIntervalMs(), GetScanBatchSize()));
Florian Mayer20c9ac72018-03-29 12:20:48 +0100302
303 file_scanner_->Scan(task_runner_);
Florian Mayer112376e2018-03-28 16:20:41 +0100304}
305
Primiano Tuccidae35652018-03-29 18:32:02 +0100306uint64_t InodeFileDataSource::GetScanIntervalMs() {
307 return OrDefault(source_config_.inode_file_config().scan_interval_ms(),
308 kScanIntervalMs);
309}
310
311uint64_t InodeFileDataSource::GetScanDelayMs() {
312 return OrDefault(source_config_.inode_file_config().scan_delay_ms(),
313 kScanDelayMs);
314}
315
316uint64_t InodeFileDataSource::GetScanBatchSize() {
317 return OrDefault(source_config_.inode_file_config().scan_batch_size(),
318 kScanBatchSize);
319}
320
Hector Dearman0ff07c72018-03-15 09:54:46 +0000321base::WeakPtr<InodeFileDataSource> InodeFileDataSource::GetWeakPtr() const {
322 return weak_factory_.GetWeakPtr();
323}
324
Anna Zappone2a6f9042018-03-14 13:26:07 +0000325} // namespace perfetto