1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "inode2filename/inode_resolver.h"
16
17 #include "common/cmd_utils.h"
18 #include "inode2filename/out_of_process_inode_resolver.h"
19 #include "inode2filename/search_directories.h"
20
21 #include <android-base/logging.h>
22
23 #include <fstream>
24 #include <stdio.h>
25
26 namespace rx = rxcpp;
27
28 namespace iorap::inode2filename {
29
ToArgs(ProcessMode process_mode)30 std::vector<std::string> ToArgs(ProcessMode process_mode) {
31 const char* value = nullptr;
32
33 switch (process_mode) {
34 case ProcessMode::kInProcessDirect:
35 value = "in";
36 break;
37 case ProcessMode::kInProcessIpc:
38 value = "in-ipc";
39 break;
40 case ProcessMode::kOutOfProcessIpc:
41 value = "out";
42 break;
43 }
44
45 std::vector<std::string> args;
46 iorap::common::AppendNamedArg(args, "--process-mode", value);
47 return args;
48 }
49
ToArgs(VerifyKind verify_kind)50 std::vector<std::string> ToArgs(VerifyKind verify_kind) {
51 const char* value = nullptr;
52
53 switch (verify_kind) {
54 case VerifyKind::kNone:
55 value = "none";
56 break;
57 case VerifyKind::kStat:
58 value = "stat";
59 break;
60 }
61
62 std::vector<std::string> args;
63 iorap::common::AppendNamedArg(args, "--verify", value);
64 return args;
65 }
66
ToArgs(const InodeResolverDependencies & deps)67 std::vector<std::string> ToArgs(const InodeResolverDependencies& deps) {
68 std::vector<std::string> args = ToArgs(*static_cast<const DataSourceDependencies*>(&deps));
69 iorap::common::AppendArgsRepeatedly(args, ToArgs(deps.process_mode));
70 iorap::common::AppendArgsRepeatedly(args, ToArgs(deps.verify));
71
72 return args;
73 }
74
75 struct InodeResolver::Impl {
Impliorap::inode2filename::InodeResolver::Impl76 Impl(InodeResolverDependencies dependencies)
77 : dependencies_{std::move(dependencies)} {
78 DCHECK(dependencies_.system_call != nullptr);
79 data_source_ = DataSource::Create(/*downcast*/dependencies_);
80 }
Impliorap::inode2filename::InodeResolver::Impl81 Impl(InodeResolverDependencies dependencies, std::shared_ptr<DataSource> data_source)
82 : dependencies_{std::move(dependencies)} {
83 DCHECK(dependencies_.system_call != nullptr);
84 data_source_ = std::move(data_source);
85 DCHECK(data_source_ != nullptr);
86 }
87 InodeResolverDependencies dependencies_;
88 std::shared_ptr<DataSource> data_source_;
89 };
90
InodeResolver(InodeResolverDependencies dependencies)91 InodeResolver::InodeResolver(InodeResolverDependencies dependencies)
92 : impl_(new InodeResolver::Impl{std::move(dependencies)}) {
93 }
94
InodeResolver(InodeResolverDependencies dependencies,std::shared_ptr<DataSource> data_source)95 InodeResolver::InodeResolver(InodeResolverDependencies dependencies,
96 std::shared_ptr<DataSource> data_source)
97 : impl_(new InodeResolver::Impl{std::move(dependencies), std::move(data_source)}) {
98 }
99
Create(InodeResolverDependencies dependencies)100 std::shared_ptr<InodeResolver> InodeResolver::Create(InodeResolverDependencies dependencies) {
101 if (dependencies.process_mode == ProcessMode::kInProcessDirect) {
102 return std::shared_ptr<InodeResolver>{
103 new InodeResolver{std::move(dependencies)}};
104 } else if (dependencies.process_mode == ProcessMode::kOutOfProcessIpc) {
105 return std::shared_ptr<InodeResolver>{
106 new OutOfProcessInodeResolver{std::move(dependencies)}};
107 } else {
108 CHECK(false);
109 }
110 return nullptr;
111 }
112
Create(InodeResolverDependencies dependencies,std::shared_ptr<DataSource> data_source)113 std::shared_ptr<InodeResolver> InodeResolver::Create(InodeResolverDependencies dependencies,
114 std::shared_ptr<DataSource> data_source) {
115 if (dependencies.process_mode == ProcessMode::kInProcessDirect) {
116 return std::shared_ptr<InodeResolver>{
117 new InodeResolver{std::move(dependencies), std::move(data_source)}};
118 } else if (dependencies.process_mode == ProcessMode::kOutOfProcessIpc) {
119 CHECK(false); // directly providing a DataSource only makes sense in-process
120 } else {
121 CHECK(false);
122 }
123 return nullptr;
124 }
125
126 rxcpp::observable<InodeResult>
FindFilenamesFromInodes(rxcpp::observable<Inode> inodes) const127 InodeResolver::FindFilenamesFromInodes(rxcpp::observable<Inode> inodes) const {
128
129 // It's inefficient to search for inodes until the full search list is available,
130 // so first reduce to a vector so we can access all the inodes simultaneously.
131 return inodes.reduce(std::vector<Inode>{},
132 [](std::vector<Inode> vec, Inode inode) {
133 vec.push_back(inode);
134 return vec;
135 },
136 [](std::vector<Inode> v) {
137 return v; // TODO: use an identity function
138 })
139 .flat_map([self=shared_from_this()](std::vector<Inode> vec) {
140 // All borrowed values (e.g. SystemCall) must outlive the observable.
141 return self->FindFilenamesFromInodes(vec);
142 }
143 );
144 }
145
146 rxcpp::observable<InodeResult>
FindFilenamesFromInodes(std::vector<Inode> inodes) const147 InodeResolver::FindFilenamesFromInodes(std::vector<Inode> inodes) const {
148 const DataSource& data_source = *impl_->data_source_;
149 const InodeResolverDependencies& dependencies = impl_->dependencies_;
150
151 // Get lazy list of inodes from the data source.
152 rxcpp::observable<InodeResult> all_inodes = impl_->data_source_->EmitInodes();
153
154 // Filter it according to the source+dependency requirements.
155 // Unsubscribe from 'all_inodes' early if all inodes are matched early.
156 const bool needs_device_number = !data_source.ResultIncludesDeviceNumber();
157 const bool needs_verification = dependencies.verify == VerifyKind::kStat;
158 SearchDirectories search{impl_->dependencies_.system_call};
159 return search.FilterFilenamesForSpecificInodes(all_inodes,
160 inodes,
161 needs_device_number,
162 needs_verification);
163 }
164
165 rxcpp::observable<InodeResult>
EmitAll() const166 InodeResolver::EmitAll() const {
167 const DataSource& data_source = *impl_->data_source_;
168 const InodeResolverDependencies& dependencies = impl_->dependencies_;
169
170 // Get lazy list of inodes from the data source.
171 rxcpp::observable<InodeResult> all_inodes = impl_->data_source_->EmitInodes();
172
173 // Apply verification and fill-in missing device numbers.
174 const bool needs_device_number = !data_source.ResultIncludesDeviceNumber();
175 const bool needs_verification = dependencies.verify == VerifyKind::kStat;
176 SearchDirectories search{impl_->dependencies_.system_call};
177 return search.EmitAllFilenames(all_inodes,
178 needs_device_number,
179 needs_verification);
180 }
181
~InodeResolver()182 InodeResolver::~InodeResolver() {
183 // std::unique_ptr requires complete types, but we hide the definition in the header.
184 delete impl_;
185 // XX: Does this work if we just force the dtor definition into the .cc file with a unique_ptr?
186 }
187
GetDependencies()188 InodeResolverDependencies& InodeResolver::GetDependencies() {
189 return impl_->dependencies_;
190 }
191
GetDependencies() const192 const InodeResolverDependencies& InodeResolver::GetDependencies() const {
193 return impl_->dependencies_;
194 }
195
StartRecording()196 void InodeResolver::StartRecording() {
197 impl_->data_source_->StartRecording();
198 }
199
StopRecording()200 void InodeResolver::StopRecording() {
201 impl_->data_source_->StopRecording();
202 }
203
204 // TODO: refactor more code from search_directories into this file.
205 // XX: do we also need a DataSink class? lets see if recording gets more complicated.
206
207 } // namespace iorap::inode2filename
208