blob: 5e2945da3b404cd12daf96ecc2fbb5a369a14798 [file] [log] [blame]
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "inode2filename/inode_resolver.h"
#include "common/cmd_utils.h"
#include "inode2filename/out_of_process_inode_resolver.h"
#include "inode2filename/search_directories.h"
#include <android-base/logging.h>
#include <fstream>
#include <stdio.h>
namespace rx = rxcpp;
namespace iorap::inode2filename {
std::vector<std::string> ToArgs(ProcessMode process_mode) {
const char* value = nullptr;
switch (process_mode) {
case ProcessMode::kInProcessDirect:
value = "in";
break;
case ProcessMode::kInProcessIpc:
value = "in-ipc";
break;
case ProcessMode::kOutOfProcessIpc:
value = "out";
break;
}
std::vector<std::string> args;
iorap::common::AppendNamedArg(args, "--process-mode", value);
return args;
}
std::vector<std::string> ToArgs(VerifyKind verify_kind) {
const char* value = nullptr;
switch (verify_kind) {
case VerifyKind::kNone:
value = "none";
break;
case VerifyKind::kStat:
value = "stat";
break;
}
std::vector<std::string> args;
iorap::common::AppendNamedArg(args, "--verify", value);
return args;
}
std::vector<std::string> ToArgs(const InodeResolverDependencies& deps) {
std::vector<std::string> args = ToArgs(*static_cast<const DataSourceDependencies*>(&deps));
iorap::common::AppendArgsRepeatedly(args, ToArgs(deps.process_mode));
iorap::common::AppendArgsRepeatedly(args, ToArgs(deps.verify));
return args;
}
struct InodeResolver::Impl {
Impl(InodeResolverDependencies dependencies)
: dependencies_{std::move(dependencies)} {
DCHECK(dependencies_.system_call != nullptr);
data_source_ = DataSource::Create(/*downcast*/dependencies_);
}
Impl(InodeResolverDependencies dependencies, std::shared_ptr<DataSource> data_source)
: dependencies_{std::move(dependencies)} {
DCHECK(dependencies_.system_call != nullptr);
data_source_ = std::move(data_source);
DCHECK(data_source_ != nullptr);
}
InodeResolverDependencies dependencies_;
std::shared_ptr<DataSource> data_source_;
};
InodeResolver::InodeResolver(InodeResolverDependencies dependencies)
: impl_(new InodeResolver::Impl{std::move(dependencies)}) {
}
InodeResolver::InodeResolver(InodeResolverDependencies dependencies,
std::shared_ptr<DataSource> data_source)
: impl_(new InodeResolver::Impl{std::move(dependencies), std::move(data_source)}) {
}
std::shared_ptr<InodeResolver> InodeResolver::Create(InodeResolverDependencies dependencies) {
if (dependencies.process_mode == ProcessMode::kInProcessDirect) {
return std::shared_ptr<InodeResolver>{
new InodeResolver{std::move(dependencies)}};
} else if (dependencies.process_mode == ProcessMode::kOutOfProcessIpc) {
return std::shared_ptr<InodeResolver>{
new OutOfProcessInodeResolver{std::move(dependencies)}};
} else {
CHECK(false);
}
return nullptr;
}
std::shared_ptr<InodeResolver> InodeResolver::Create(InodeResolverDependencies dependencies,
std::shared_ptr<DataSource> data_source) {
if (dependencies.process_mode == ProcessMode::kInProcessDirect) {
return std::shared_ptr<InodeResolver>{
new InodeResolver{std::move(dependencies), std::move(data_source)}};
} else if (dependencies.process_mode == ProcessMode::kOutOfProcessIpc) {
CHECK(false); // directly providing a DataSource only makes sense in-process
} else {
CHECK(false);
}
return nullptr;
}
rxcpp::observable<InodeResult>
InodeResolver::FindFilenamesFromInodes(rxcpp::observable<Inode> inodes) const {
// It's inefficient to search for inodes until the full search list is available,
// so first reduce to a vector so we can access all the inodes simultaneously.
return inodes.reduce(std::vector<Inode>{},
[](std::vector<Inode> vec, Inode inode) {
vec.push_back(inode);
return vec;
},
[](std::vector<Inode> v) {
return v; // TODO: use an identity function
})
.flat_map([self=shared_from_this()](std::vector<Inode> vec) {
// All borrowed values (e.g. SystemCall) must outlive the observable.
return self->FindFilenamesFromInodes(vec);
}
);
}
rxcpp::observable<InodeResult>
InodeResolver::FindFilenamesFromInodes(std::vector<Inode> inodes) const {
const DataSource& data_source = *impl_->data_source_;
const InodeResolverDependencies& dependencies = impl_->dependencies_;
// Get lazy list of inodes from the data source.
rxcpp::observable<InodeResult> all_inodes = impl_->data_source_->EmitInodes();
// Filter it according to the source+dependency requirements.
// Unsubscribe from 'all_inodes' early if all inodes are matched early.
const bool needs_device_number = !data_source.ResultIncludesDeviceNumber();
const bool needs_verification = dependencies.verify == VerifyKind::kStat;
SearchDirectories search{impl_->dependencies_.system_call};
return search.FilterFilenamesForSpecificInodes(all_inodes,
inodes,
needs_device_number,
needs_verification);
}
rxcpp::observable<InodeResult>
InodeResolver::EmitAll() const {
const DataSource& data_source = *impl_->data_source_;
const InodeResolverDependencies& dependencies = impl_->dependencies_;
// Get lazy list of inodes from the data source.
rxcpp::observable<InodeResult> all_inodes = impl_->data_source_->EmitInodes();
// Apply verification and fill-in missing device numbers.
const bool needs_device_number = !data_source.ResultIncludesDeviceNumber();
const bool needs_verification = dependencies.verify == VerifyKind::kStat;
SearchDirectories search{impl_->dependencies_.system_call};
return search.EmitAllFilenames(all_inodes,
needs_device_number,
needs_verification);
}
InodeResolver::~InodeResolver() {
// std::unique_ptr requires complete types, but we hide the definition in the header.
delete impl_;
// XX: Does this work if we just force the dtor definition into the .cc file with a unique_ptr?
}
InodeResolverDependencies& InodeResolver::GetDependencies() {
return impl_->dependencies_;
}
const InodeResolverDependencies& InodeResolver::GetDependencies() const {
return impl_->dependencies_;
}
void InodeResolver::StartRecording() {
impl_->data_source_->StartRecording();
}
void InodeResolver::StopRecording() {
impl_->data_source_->StopRecording();
}
// TODO: refactor more code from search_directories into this file.
// XX: do we also need a DataSink class? lets see if recording gets more complicated.
} // namespace iorap::inode2filename