// 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 "common/cmd_utils.h" #include "db/file_models.h" #include "db/models.h" #include #include #include #include #include #include #include #include namespace iorap::db { static constexpr const char* kRootPathProp = "iorapd.root.dir"; static const unsigned int kPerfettoMaxTraces = ::android::base::GetUintProperty("iorapd.perfetto.max_traces", /*default*/2u); static uint64_t GetTimeNanoseconds() { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); uint64_t now_ns = (now.tv_sec * 1000000000LL + now.tv_nsec); return now_ns; } static bool IsDir(const std::string& dirpath) { struct stat st; if (stat(dirpath.c_str(), &st) == 0) { if (S_ISDIR(st.st_mode)) { return true; } } return false; } // Given some path /a/b/c create all of /a, /a/b, /a/b/c/ recursively. static bool MkdirWithParents(const std::string& path) { size_t prev_end = 0; while (prev_end < path.size()) { size_t next_end = path.find('/', prev_end + 1); std::string dir_path = path.substr(0, next_end); if (!IsDir(dir_path)) { #if defined(_WIN32) int ret = mkdir(dir_path.c_str()); #else mode_t old_mask = umask(0); // The user permission is 5 to allow system server to // read the files. No other users could do that because // the upper directory only allows system server and iorapd // to access. Also selinux rules prevent other users to // read files here. int ret = mkdir(dir_path.c_str(), 0755); umask(old_mask); #endif if (ret != 0) { PLOG(ERROR) << "failed to create dir " << dir_path; return false; } } prev_end = next_end; if (next_end == std::string::npos) { break; } } return true; } FileModelBase::FileModelBase(VersionedComponentName vcn) : vcn_{std::move(vcn)} { root_path_ = common::GetEnvOrProperty(kRootPathProp, /*default*/"/data/misc/iorapd"); } std::string FileModelBase::BaseDir() const { std::stringstream ss; ss << root_path_ << "/" << vcn_.GetPackage() << "/"; ss << vcn_.GetVersion(); ss << "/"; ss << vcn_.GetActivity() << "/"; ss << SubDir(); return ss.str(); } std::string FileModelBase::FilePath() const { std::stringstream ss; ss << BaseDir(); ss << "/"; ss << BaseFile(); return ss.str(); } bool FileModelBase::MkdirWithParents() { LOG(VERBOSE) << "MkdirWithParents: " << BaseDir(); return ::iorap::db::MkdirWithParents(BaseDir()); } PerfettoTraceFileModel::PerfettoTraceFileModel(VersionedComponentName vcn, uint64_t timestamp) : FileModelBase{std::move(vcn)}, timestamp_{timestamp} { } PerfettoTraceFileModel PerfettoTraceFileModel::CalculateNewestFilePath(VersionedComponentName vcn) { uint64_t timestamp = GetTimeNanoseconds(); return PerfettoTraceFileModel{vcn, timestamp}; } std::string PerfettoTraceFileModel::BaseFile() const { std::stringstream ss; ss << timestamp_ << ".perfetto_trace.pb"; return ss.str(); } bool PerfettoTraceFileModel::NeedMorePerfettoTraces(DbHandle& db, VersionedComponentName vcn) { std::vector raw_traces = RawTraceModel::SelectByVersionedComponentName(db, vcn); size_t raw_traces_size = raw_traces.size(); LOG(VERBOSE) << "The number of perfetto traces is " << raw_traces_size << " The cap is " << kPerfettoMaxTraces ; return raw_traces_size < kPerfettoMaxTraces; } void PerfettoTraceFileModel::DeleteOlderFiles(DbHandle& db, VersionedComponentName vcn) { std::vector raw_traces = RawTraceModel::SelectByVersionedComponentName(db, vcn); if (WOULD_LOG(VERBOSE)) { size_t raw_traces_size = raw_traces.size(); for (size_t i = 0; i < raw_traces_size; ++i) { LOG(VERBOSE) << "DeleteOlderFiles - selected " << raw_traces[i]; } LOG(VERBOSE) << "DeleteOlderFiles - queried total " << raw_traces_size << " records"; } size_t items_to_delete = 0; if (raw_traces.size() > kPerfettoMaxTraces) { items_to_delete = raw_traces.size() - kPerfettoMaxTraces; } else { LOG(VERBOSE) << "DeleteOlderFiles - don't delete older raw traces, too few files: " << " wanted at least " << kPerfettoMaxTraces << ", but got " << raw_traces.size(); } for (size_t i = 0; i < items_to_delete; ++i) { RawTraceModel& raw_trace = raw_traces[i]; // sorted ascending -> items to delete are first. std::string err_msg; if (!::android::base::RemoveFileIfExists(raw_trace.file_path, /*out*/&err_msg)) { LOG(ERROR) << "Failed to remove raw trace file: " << raw_trace.file_path << ", reason: " << err_msg; } else { raw_trace.Delete(); LOG(DEBUG) << "Deleted raw trace for " << vcn << " at " << raw_trace.file_path; } } } CompiledTraceFileModel::CompiledTraceFileModel(VersionedComponentName vcn) : FileModelBase{std::move(vcn)} { } CompiledTraceFileModel CompiledTraceFileModel::CalculateNewestFilePath(VersionedComponentName vcn) { return CompiledTraceFileModel{vcn}; } std::string CompiledTraceFileModel::BaseFile() const { return "compiled_trace.pb"; } } // namespace iorap::db