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 "perfetto/trace/filesystem/inode_file_map.pbzero.h"
20 #include "src/base/test/test_task_runner.h"
21 #include "src/traced/probes/filesystem/lru_inode_cache.h"
22 #include "src/tracing/core/null_trace_writer.h"
23
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26
27 namespace perfetto {
28 namespace {
29
30 using ::testing::Eq;
31 using ::testing::InvokeWithoutArgs;
32 using ::testing::IsNull;
33 using ::testing::Pointee;
34 using ::testing::_;
35
36 class TestInodeFileDataSource : public InodeFileDataSource {
37 public:
TestInodeFileDataSource(DataSourceConfig cfg,base::TaskRunner * task_runner,TracingSessionID tsid,std::map<BlockDeviceID,std::unordered_map<Inode,InodeMapValue>> * static_file_map,LRUInodeCache * cache,std::unique_ptr<TraceWriter> writer)38 TestInodeFileDataSource(
39 DataSourceConfig cfg,
40 base::TaskRunner* task_runner,
41 TracingSessionID tsid,
42 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
43 static_file_map,
44 LRUInodeCache* cache,
45 std::unique_ptr<TraceWriter> writer)
46 : InodeFileDataSource(std::move(cfg),
47 task_runner,
48 tsid,
49 static_file_map,
50 cache,
51 std::move(writer)) {
52 struct stat buf;
53 PERFETTO_CHECK(lstat("src/traced/probes/filesystem/testdata", &buf) != -1);
54 mount_points_.emplace(buf.st_dev, "src/traced/probes/filesystem/testdata");
55 }
56
57 MOCK_METHOD3(FillInodeEntry,
58 void(InodeFileMap* destination,
59 Inode inode_number,
60 const InodeMapValue& inode_map_value));
61 };
62
63 class InodeFileDataSourceTest : public ::testing::Test {
64 protected:
InodeFileDataSourceTest()65 InodeFileDataSourceTest() {}
66
GetInodeFileDataSource(DataSourceConfig cfg)67 std::unique_ptr<TestInodeFileDataSource> GetInodeFileDataSource(
68 DataSourceConfig cfg) {
69 return std::unique_ptr<TestInodeFileDataSource>(new TestInodeFileDataSource(
70 cfg, &task_runner_, 0, &static_file_map_, &cache_,
71 std::unique_ptr<NullTraceWriter>(new NullTraceWriter)));
72 }
73
74 LRUInodeCache cache_{100};
75 std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>
76 static_file_map_;
77 base::TestTaskRunner task_runner_;
78 };
79
TEST_F(InodeFileDataSourceTest,TestFileSystemScan)80 TEST_F(InodeFileDataSourceTest, TestFileSystemScan) {
81 DataSourceConfig config;
82 config.mutable_inode_file_config()->set_scan_interval_ms(1);
83 config.mutable_inode_file_config()->set_scan_delay_ms(1);
84 auto data_source = GetInodeFileDataSource(config);
85
86 struct stat buf;
87 PERFETTO_CHECK(lstat("src/traced/probes/filesystem/testdata/file2", &buf) !=
88 -1);
89
90 auto done = task_runner_.CreateCheckpoint("done");
91 InodeMapValue value(protos::pbzero::InodeFileMap_Entry_Type_FILE,
92 {"src/traced/probes/filesystem/testdata/file2"});
93 EXPECT_CALL(*data_source, FillInodeEntry(_, buf.st_ino, Eq(value)))
94 .WillOnce(InvokeWithoutArgs(done));
95
96 data_source->OnInodes({{buf.st_ino, buf.st_dev}});
97 task_runner_.RunUntilCheckpoint("done");
98
99 // Expect that the found inode is added the the LRU cache.
100 EXPECT_THAT(cache_.Get(std::make_pair(buf.st_dev, buf.st_ino)),
101 Pointee(Eq(value)));
102 }
103
TEST_F(InodeFileDataSourceTest,TestStaticMap)104 TEST_F(InodeFileDataSourceTest, TestStaticMap) {
105 DataSourceConfig config;
106 auto data_source = GetInodeFileDataSource(config);
107 CreateStaticDeviceToInodeMap("src/traced/probes/filesystem/testdata",
108 &static_file_map_);
109
110 struct stat buf;
111 PERFETTO_CHECK(lstat("src/traced/probes/filesystem/testdata/file2", &buf) !=
112 -1);
113
114 InodeMapValue value(protos::pbzero::InodeFileMap_Entry_Type_FILE,
115 {"src/traced/probes/filesystem/testdata/file2"});
116 EXPECT_CALL(*data_source, FillInodeEntry(_, buf.st_ino, Eq(value)));
117
118 data_source->OnInodes({{buf.st_ino, buf.st_dev}});
119 // Expect that the found inode is not added the the LRU cache.
120 EXPECT_THAT(cache_.Get(std::make_pair(buf.st_dev, buf.st_ino)), IsNull());
121 }
122
TEST_F(InodeFileDataSourceTest,TestCache)123 TEST_F(InodeFileDataSourceTest, TestCache) {
124 DataSourceConfig config;
125 auto data_source = GetInodeFileDataSource(config);
126 CreateStaticDeviceToInodeMap("src/traced/probes/filesystem/testdata",
127 &static_file_map_);
128
129 struct stat buf;
130 PERFETTO_CHECK(lstat("src/traced/probes/filesystem/testdata/file2", &buf) !=
131 -1);
132
133 InodeMapValue value(protos::pbzero::InodeFileMap_Entry_Type_FILE,
134 {"src/traced/probes/filesystem/testdata/file2"});
135 cache_.Insert(std::make_pair(buf.st_dev, buf.st_ino), value);
136
137 EXPECT_CALL(*data_source, FillInodeEntry(_, buf.st_ino, Eq(value)));
138
139 data_source->OnInodes({{buf.st_ino, buf.st_dev}});
140 }
141
142 } // namespace
143 } // namespace perfetto
144