• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <queue>
24 #include <unordered_map>
25 
26 #include "perfetto/base/logging.h"
27 #include "perfetto/base/scoped_file.h"
28 #include "perfetto/tracing/core/trace_packet.h"
29 #include "perfetto/tracing/core/trace_writer.h"
30 
31 #include "perfetto/trace/trace_packet.pbzero.h"
32 #include "src/traced/probes/filesystem/file_scanner.h"
33 
34 namespace perfetto {
35 namespace {
36 constexpr uint32_t kScanIntervalMs = 10000;  // 10s
37 constexpr uint32_t kScanDelayMs = 10000;     // 10s
38 constexpr uint32_t kScanBatchSize = 15000;
39 
OrDefault(uint32_t value,uint32_t def)40 uint32_t OrDefault(uint32_t value, uint32_t def) {
41   return value ? value : def;
42 }
43 
DbgFmt(const std::vector<std::string> & values)44 std::string DbgFmt(const std::vector<std::string>& values) {
45   if (values.empty())
46     return "";
47 
48   std::string result;
49   for (auto it = values.cbegin(); it != values.cend() - 1; ++it)
50     result += *it + ",";
51 
52   result += values.back();
53   return result;
54 }
55 
BuildMountpointMapping(const DataSourceConfig & source_config)56 std::map<std::string, std::vector<std::string>> BuildMountpointMapping(
57     const DataSourceConfig& source_config) {
58   std::map<std::string, std::vector<std::string>> m;
59   for (const auto& map_entry :
60        source_config.inode_file_config().mount_point_mapping())
61     m.emplace(map_entry.mountpoint(), map_entry.scan_roots());
62 
63   return m;
64 }
65 
66 class StaticMapDelegate : public FileScanner::Delegate {
67  public:
StaticMapDelegate(std::map<BlockDeviceID,std::unordered_map<Inode,InodeMapValue>> * map)68   StaticMapDelegate(
69       std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map)
70       : map_(map) {}
~StaticMapDelegate()71   ~StaticMapDelegate() {}
72 
73  private:
OnInodeFound(BlockDeviceID block_device_id,Inode inode_number,const std::string & path,protos::pbzero::InodeFileMap_Entry_Type type)74   bool OnInodeFound(BlockDeviceID block_device_id,
75                     Inode inode_number,
76                     const std::string& path,
77                     protos::pbzero::InodeFileMap_Entry_Type type) {
78     std::unordered_map<Inode, InodeMapValue>& inode_map =
79         (*map_)[block_device_id];
80     inode_map[inode_number].SetType(type);
81     inode_map[inode_number].AddPath(path);
82     return true;
83   }
OnInodeScanDone()84   void OnInodeScanDone() {}
85   std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map_;
86 };
87 
88 }  // namespace
89 
90 // static
91 constexpr int InodeFileDataSource::kTypeId;
92 
CreateStaticDeviceToInodeMap(const std::string & root_directory,std::map<BlockDeviceID,std::unordered_map<Inode,InodeMapValue>> * static_file_map)93 void CreateStaticDeviceToInodeMap(
94     const std::string& root_directory,
95     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
96         static_file_map) {
97   StaticMapDelegate delegate(static_file_map);
98   FileScanner scanner({root_directory}, &delegate);
99   scanner.Scan();
100 }
101 
FillInodeEntry(InodeFileMap * destination,Inode inode_number,const InodeMapValue & inode_map_value)102 void InodeFileDataSource::FillInodeEntry(InodeFileMap* destination,
103                                          Inode inode_number,
104                                          const InodeMapValue& inode_map_value) {
105   auto* entry = destination->add_entries();
106   entry->set_inode_number(inode_number);
107   entry->set_type(inode_map_value.type());
108   for (const auto& path : inode_map_value.paths())
109     entry->add_paths(path.c_str());
110 }
111 
InodeFileDataSource(DataSourceConfig source_config,base::TaskRunner * task_runner,TracingSessionID session_id,std::map<BlockDeviceID,std::unordered_map<Inode,InodeMapValue>> * static_file_map,LRUInodeCache * cache,std::unique_ptr<TraceWriter> writer)112 InodeFileDataSource::InodeFileDataSource(
113     DataSourceConfig source_config,
114     base::TaskRunner* task_runner,
115     TracingSessionID session_id,
116     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
117         static_file_map,
118     LRUInodeCache* cache,
119     std::unique_ptr<TraceWriter> writer)
120     : ProbesDataSource(session_id, kTypeId),
121       source_config_(std::move(source_config)),
122       scan_mount_points_(
123           source_config_.inode_file_config().scan_mount_points().cbegin(),
124           source_config_.inode_file_config().scan_mount_points().cend()),
125       mount_point_mapping_(BuildMountpointMapping(source_config_)),
126       task_runner_(task_runner),
127       static_file_map_(static_file_map),
128       cache_(cache),
129       writer_(std::move(writer)),
130       weak_factory_(this) {}
131 
132 InodeFileDataSource::~InodeFileDataSource() = default;
133 
Start()134 void InodeFileDataSource::Start() {
135   // Nothing special to do, this data source is only reacting to on-demand
136   // events such as OnInodes().
137 }
138 
AddInodesFromStaticMap(BlockDeviceID block_device_id,std::set<Inode> * inode_numbers)139 void InodeFileDataSource::AddInodesFromStaticMap(
140     BlockDeviceID block_device_id,
141     std::set<Inode>* inode_numbers) {
142   // Check if block device id exists in static file map
143   auto static_map_entry = static_file_map_->find(block_device_id);
144   if (static_map_entry == static_file_map_->end())
145     return;
146 
147   uint64_t system_found_count = 0;
148   for (auto it = inode_numbers->begin(); it != inode_numbers->end();) {
149     Inode inode_number = *it;
150     // Check if inode number exists in static file map for given block device id
151     auto inode_it = static_map_entry->second.find(inode_number);
152     if (inode_it == static_map_entry->second.end()) {
153       ++it;
154       continue;
155     }
156     system_found_count++;
157     it = inode_numbers->erase(it);
158     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
159                    inode_it->second);
160   }
161   PERFETTO_DLOG("%" PRIu64 " inodes found in static file map",
162                 system_found_count);
163 }
164 
AddInodesFromLRUCache(BlockDeviceID block_device_id,std::set<Inode> * inode_numbers)165 void InodeFileDataSource::AddInodesFromLRUCache(
166     BlockDeviceID block_device_id,
167     std::set<Inode>* inode_numbers) {
168   uint64_t cache_found_count = 0;
169   for (auto it = inode_numbers->begin(); it != inode_numbers->end();) {
170     Inode inode_number = *it;
171     auto value = cache_->Get(std::make_pair(block_device_id, inode_number));
172     if (value == nullptr) {
173       ++it;
174       continue;
175     }
176     cache_found_count++;
177     it = inode_numbers->erase(it);
178     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
179                    *value);
180   }
181   if (cache_found_count > 0)
182     PERFETTO_DLOG("%" PRIu64 " inodes found in cache", cache_found_count);
183 }
184 
Flush(FlushRequestID,std::function<void ()> callback)185 void InodeFileDataSource::Flush(FlushRequestID,
186                                 std::function<void()> callback) {
187   ResetTracePacket();
188   writer_->Flush(callback);
189 }
190 
OnInodes(const std::vector<std::pair<Inode,BlockDeviceID>> & inodes)191 void InodeFileDataSource::OnInodes(
192     const std::vector<std::pair<Inode, BlockDeviceID>>& inodes) {
193   if (mount_points_.empty()) {
194     mount_points_ = ParseMounts();
195   }
196   // Group inodes from FtraceMetadata by block device
197   std::map<BlockDeviceID, std::set<Inode>> inode_file_maps;
198   for (const auto& inodes_pair : inodes) {
199     Inode inode_number = inodes_pair.first;
200     BlockDeviceID block_device_id = inodes_pair.second;
201     inode_file_maps[block_device_id].emplace(inode_number);
202   }
203   if (inode_file_maps.size() > 1)
204     PERFETTO_DLOG("Saw %zu block devices.", inode_file_maps.size());
205 
206   // Write a TracePacket with an InodeFileMap proto for each block device id
207   for (auto& inode_file_map_data : inode_file_maps) {
208     BlockDeviceID block_device_id = inode_file_map_data.first;
209     std::set<Inode>& inode_numbers = inode_file_map_data.second;
210     PERFETTO_DLOG("Saw %zu unique inode numbers.", inode_numbers.size());
211 
212     // Add entries to InodeFileMap as inodes are found and resolved to their
213     // paths/type
214     AddInodesFromStaticMap(block_device_id, &inode_numbers);
215     AddInodesFromLRUCache(block_device_id, &inode_numbers);
216 
217     if (source_config_.inode_file_config().do_not_scan())
218       inode_numbers.clear();
219 
220     // If we defined mount points we want to scan in the config,
221     // skip inodes on other mount points.
222     if (!scan_mount_points_.empty()) {
223       bool process = true;
224       auto range = mount_points_.equal_range(block_device_id);
225       for (auto it = range.first; it != range.second; ++it) {
226         if (scan_mount_points_.count(it->second) == 0) {
227           process = false;
228           break;
229         }
230       }
231       if (!process)
232         continue;
233     }
234 
235     if (!inode_numbers.empty()) {
236       // Try to piggy back the current scan.
237       auto it = missing_inodes_.find(block_device_id);
238       if (it != missing_inodes_.end()) {
239         it->second.insert(inode_numbers.cbegin(), inode_numbers.cend());
240       }
241       next_missing_inodes_[block_device_id].insert(inode_numbers.cbegin(),
242                                                    inode_numbers.cend());
243       if (!scan_running_) {
244         scan_running_ = true;
245         auto weak_this = GetWeakPtr();
246         task_runner_->PostDelayedTask(
247             [weak_this] {
248               if (!weak_this) {
249                 PERFETTO_DLOG("Giving up filesystem scan.");
250                 return;
251               }
252               weak_this.get()->FindMissingInodes();
253             },
254             GetScanDelayMs());
255       }
256     }
257   }
258 }
259 
AddToCurrentTracePacket(BlockDeviceID block_device_id)260 InodeFileMap* InodeFileDataSource::AddToCurrentTracePacket(
261     BlockDeviceID block_device_id) {
262   seen_block_devices_.emplace(block_device_id);
263   if (!has_current_trace_packet_ ||
264       current_block_device_id_ != block_device_id) {
265     if (has_current_trace_packet_)
266       current_trace_packet_->Finalize();
267     current_trace_packet_ = writer_->NewTracePacket();
268     current_file_map_ = current_trace_packet_->set_inode_file_map();
269     has_current_trace_packet_ = true;
270 
271     // Add block device id to InodeFileMap
272     current_file_map_->set_block_device_id(
273         static_cast<uint64_t>(block_device_id));
274     // Add mount points to InodeFileMap
275     auto range = mount_points_.equal_range(block_device_id);
276     for (std::multimap<BlockDeviceID, std::string>::iterator it = range.first;
277          it != range.second; ++it)
278       current_file_map_->add_mount_points(it->second.c_str());
279   }
280   return current_file_map_;
281 }
282 
RemoveFromNextMissingInodes(BlockDeviceID block_device_id,Inode inode_number)283 void InodeFileDataSource::RemoveFromNextMissingInodes(
284     BlockDeviceID block_device_id,
285     Inode inode_number) {
286   auto it = next_missing_inodes_.find(block_device_id);
287   if (it == next_missing_inodes_.end())
288     return;
289   it->second.erase(inode_number);
290 }
291 
OnInodeFound(BlockDeviceID block_device_id,Inode inode_number,const std::string & path,protos::pbzero::InodeFileMap_Entry_Type type)292 bool InodeFileDataSource::OnInodeFound(
293     BlockDeviceID block_device_id,
294     Inode inode_number,
295     const std::string& path,
296     protos::pbzero::InodeFileMap_Entry_Type type) {
297   auto it = missing_inodes_.find(block_device_id);
298   if (it == missing_inodes_.end())
299     return true;
300 
301   size_t n = it->second.erase(inode_number);
302   if (n == 0)
303     return true;
304 
305   if (it->second.empty())
306     missing_inodes_.erase(it);
307 
308   RemoveFromNextMissingInodes(block_device_id, inode_number);
309 
310   std::pair<BlockDeviceID, Inode> key{block_device_id, inode_number};
311   auto cur_val = cache_->Get(key);
312   if (cur_val) {
313     cur_val->AddPath(path);
314     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
315                    *cur_val);
316   } else {
317     InodeMapValue new_val(InodeMapValue(type, {path}));
318     cache_->Insert(key, new_val);
319     FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
320                    new_val);
321   }
322   PERFETTO_DLOG("Filled %s", path.c_str());
323   return !missing_inodes_.empty();
324 }
325 
ResetTracePacket()326 void InodeFileDataSource::ResetTracePacket() {
327   current_block_device_id_ = 0;
328   current_file_map_ = nullptr;
329   if (has_current_trace_packet_)
330     current_trace_packet_->Finalize();
331   has_current_trace_packet_ = false;
332 }
333 
OnInodeScanDone()334 void InodeFileDataSource::OnInodeScanDone() {
335   // Finalize the accumulated trace packets.
336   ResetTracePacket();
337   file_scanner_.reset();
338   if (!missing_inodes_.empty()) {
339     // At least write mount point mapping for inodes that are not found.
340     for (const auto& p : missing_inodes_) {
341       if (seen_block_devices_.count(p.first) == 0)
342         AddToCurrentTracePacket(p.first);
343     }
344   }
345 
346   if (next_missing_inodes_.empty()) {
347     scan_running_ = false;
348   } else {
349     auto weak_this = GetWeakPtr();
350     PERFETTO_DLOG("Starting another filesystem scan.");
351     task_runner_->PostDelayedTask(
352         [weak_this] {
353           if (!weak_this) {
354             PERFETTO_DLOG("Giving up filesystem scan.");
355             return;
356           }
357           weak_this->FindMissingInodes();
358         },
359         GetScanDelayMs());
360   }
361 }
362 
AddRootsForBlockDevice(BlockDeviceID block_device_id,std::vector<std::string> * roots)363 void InodeFileDataSource::AddRootsForBlockDevice(
364     BlockDeviceID block_device_id,
365     std::vector<std::string>* roots) {
366   auto range = mount_points_.equal_range(block_device_id);
367   for (auto it = range.first; it != range.second; ++it) {
368     PERFETTO_DLOG("Trying to replace %s", it->second.c_str());
369     auto replace_it = mount_point_mapping_.find(it->second);
370     if (replace_it != mount_point_mapping_.end()) {
371       roots->insert(roots->end(), replace_it->second.cbegin(),
372                     replace_it->second.cend());
373       return;
374     }
375   }
376 
377   for (auto it = range.first; it != range.second; ++it)
378     roots->emplace_back(it->second);
379 }
380 
FindMissingInodes()381 void InodeFileDataSource::FindMissingInodes() {
382   missing_inodes_ = std::move(next_missing_inodes_);
383   std::vector<std::string> roots;
384   for (auto& p : missing_inodes_)
385     AddRootsForBlockDevice(p.first, &roots);
386 
387   PERFETTO_DCHECK(file_scanner_.get() == nullptr);
388   auto weak_this = GetWeakPtr();
389   PERFETTO_DLOG("Starting scan of %s", DbgFmt(roots).c_str());
390   file_scanner_ = std::unique_ptr<FileScanner>(new FileScanner(
391       std::move(roots), this, GetScanIntervalMs(), GetScanBatchSize()));
392 
393   file_scanner_->Scan(task_runner_);
394 }
395 
GetScanIntervalMs() const396 uint32_t InodeFileDataSource::GetScanIntervalMs() const {
397   return OrDefault(source_config_.inode_file_config().scan_interval_ms(),
398                    kScanIntervalMs);
399 }
400 
GetScanDelayMs() const401 uint32_t InodeFileDataSource::GetScanDelayMs() const {
402   return OrDefault(source_config_.inode_file_config().scan_delay_ms(),
403                    kScanDelayMs);
404 }
405 
GetScanBatchSize() const406 uint32_t InodeFileDataSource::GetScanBatchSize() const {
407   return OrDefault(source_config_.inode_file_config().scan_batch_size(),
408                    kScanBatchSize);
409 }
410 
GetWeakPtr() const411 base::WeakPtr<InodeFileDataSource> InodeFileDataSource::GetWeakPtr() const {
412   return weak_factory_.GetWeakPtr();
413 }
414 
415 }  // namespace perfetto
416