iorap: Add version support to iorap.
To avoid package query IPC overhead, when iorapd is starting, all
package version are cached (~80ms) in RAM for faster queries. If the app
in newly installed and the version is not cached, one IPC call is
made with a cost ~0.6ms and the result is cached.
The RAM versions is updated during maintenance.
Details:
common: Add version support to iorap.
manager: Add version support for perfetto trace insertion.
maintenance: Update the package versions during maintenance.
maintenance: Delete older version packages and relevant data and files.
maintenance: Add version to compilation in maintenance.
db: Add delete cascade.
Bug: 137785555
Test: Run on the device and the vesrion is shown in database after
insertion.
Change-Id: If9a2f68888d81fa7be34ef6e31a0a972f7548f3b
diff --git a/src/db/clean_up.cc b/src/db/clean_up.cc
new file mode 100644
index 0000000..b3eba46
--- /dev/null
+++ b/src/db/clean_up.cc
@@ -0,0 +1,78 @@
+// Copyright (C) 2020 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 "db/clean_up.h"
+
+#include <android-base/file.h>
+
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "db/file_models.h"
+#include "db/models.h"
+
+namespace iorap::db {
+
+void CleanUpFilesForActivity(const db::DbHandle& db,
+ const db::VersionedComponentName& vcn) {
+ LOG(DEBUG) << "Clean up files for activity " << vcn.GetActivity();
+ // Remove perfetto traces.
+ std::vector<db::RawTraceModel> raw_traces =
+ db::RawTraceModel::SelectByVersionedComponentName(db, vcn);
+ for (db::RawTraceModel raw_trace : raw_traces) {
+ std::string file_path = raw_trace.file_path;
+ LOG(DEBUG) << "Remove file: " << file_path;
+ std::filesystem::remove(file_path.c_str());
+ }
+
+ // Remove compiled traces.
+ std::optional<db::PrefetchFileModel> prefetch_file =
+ db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
+
+ if (prefetch_file) {
+ std::string file_path = prefetch_file->file_path;
+ LOG(DEBUG) << "Remove file: " << file_path;
+ std::filesystem::remove(file_path.c_str());
+ }
+}
+
+void CleanUpFilesForPackage(const db::DbHandle& db,
+ int package_id,
+ const std::string& package_name,
+ int64_t version) {
+ LOG(DEBUG) << "Clean up files for package " << package_name << " with version "
+ << version;
+ std::vector<db::ActivityModel> activities =
+ db::ActivityModel::SelectByPackageId(db, package_id);
+
+ for (db::ActivityModel activity : activities) {
+ db::VersionedComponentName vcn{package_name, activity.name, version};
+ CleanUpFilesForActivity(db, vcn);
+ }
+}
+
+void CleanUpFilesForDb(const db::DbHandle& db) {
+ std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
+ for (db::PackageModel package : packages) {
+ CleanUpFilesForPackage(db, package.id, package.name, package.version);
+ }
+}
+
+} // namespace iorap::db
diff --git a/src/db/clean_up.h b/src/db/clean_up.h
new file mode 100644
index 0000000..82a26fd
--- /dev/null
+++ b/src/db/clean_up.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 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.
+
+#ifndef IORAP_SRC_DB_CLEANER_H_
+#define IORAP_SRC_DB_CLEANER_H_
+
+#include <android/content/pm/IPackageManagerNative.h>
+
+#include <string>
+#include <vector>
+
+#include "binder/package_version_map.h"
+#include "db/file_models.h"
+
+namespace iorap::db {
+
+// Clean up perfetto traces and compiled traces stored in the db.
+void CleanUpFilesForDb(const db::DbHandle& db);
+
+void CleanUpFilesForPackage(const db::DbHandle& db,
+ int package_id,
+ const std::string& package_name,
+ int64_t version);
+} // namespace iorap::db
+
+#endif // IORAP_SRC_DB_CLEANER_H_
diff --git a/src/db/file_models.cc b/src/db/file_models.cc
index f47c68b..9a0c360 100644
--- a/src/db/file_models.cc
+++ b/src/db/file_models.cc
@@ -87,11 +87,7 @@
std::stringstream ss;
ss << root_path_ << "/" << vcn_.GetPackage() << "/";
- if (vcn_.GetVersion()) {
- ss << *vcn_.GetVersion();
- } else {
- ss << "none";
- }
+ ss << vcn_.GetVersion();
ss << "/";
ss << vcn_.GetActivity() << "/";
ss << SubDir();
@@ -131,9 +127,7 @@
void PerfettoTraceFileModel::DeleteOlderFiles(DbHandle& db, VersionedComponentName vcn) {
std::vector<RawTraceModel> raw_traces =
- RawTraceModel::SelectByPackageNameActivityName(db,
- vcn.GetPackage(),
- vcn.GetActivity()); // TODO: version
+ RawTraceModel::SelectByVersionedComponentName(db, vcn);
if (WOULD_LOG(VERBOSE)) {
size_t raw_traces_size = raw_traces.size();
diff --git a/src/db/file_models.h b/src/db/file_models.h
index 0a2c24f..135c98a 100644
--- a/src/db/file_models.h
+++ b/src/db/file_models.h
@@ -24,7 +24,7 @@
struct VersionedComponentName {
VersionedComponentName(std::string package,
std::string activity,
- std::optional<int> version)
+ int64_t version)
: package_{std::move(package)},
activity_{std::move(activity)},
version_{version} {
@@ -38,23 +38,18 @@
return activity_;
}
- std::optional<int> GetVersion() const {
+ int GetVersion() const {
return version_;
}
private:
std::string package_;
std::string activity_;
- std::optional<int> version_;
+ int64_t version_;
};
inline std::ostream& operator<<(std::ostream& os, const VersionedComponentName& vcn) {
- os << vcn.GetPackage() << "/" << vcn.GetActivity() << "@";
- if (vcn.GetVersion()) {
- os << "none";
- } else {
- os << *vcn.GetVersion();
- }
+ os << vcn.GetPackage() << "/" << vcn.GetActivity() << "@" << vcn.GetVersion();
return os;
}
diff --git a/src/db/main.cc b/src/db/main.cc
index 7b64756..2edd961 100644
--- a/src/db/main.cc
+++ b/src/db/main.cc
@@ -54,6 +54,8 @@
LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg;
}
+const constexpr int64_t kNoVersion = -1;
+
int Main(int argc, char** argv) {
// Go to system logcat + stderr when running from command line.
android::base::InitLogging(argv, iorap::common::StderrAndLogdLogger{android::base::SYSTEM});
@@ -186,7 +188,7 @@
std::optional<ActivityModel> activity =
ActivityModel::SelectOrInsert(db,
component_name.package,
- /*version*/std::nullopt,
+ kNoVersion,
component_name.activity_name);
DCHECK(activity.has_value());
LOG(DEBUG) << "Component selected/inserted: " << *activity;
@@ -204,7 +206,7 @@
std::optional<ActivityModel> activity =
ActivityModel::SelectOrInsert(db,
component_name.package,
- /*version*/std::nullopt,
+ kNoVersion,
component_name.activity_name);
DCHECK(activity.has_value());
diff --git a/src/db/models.h b/src/db/models.h
index 90acfc1..bfe8cfc 100644
--- a/src/db/models.h
+++ b/src/db/models.h
@@ -15,8 +15,14 @@
#ifndef IORAP_SRC_DB_MODELS_H_
#define IORAP_SRC_DB_MODELS_H_
-#include <android-base/logging.h>
+#include "clean_up.h"
+#include "file_models.h"
+#include <android-base/logging.h>
+#include <utils/String8.h>
+
+#include <filesystem>
+#include <iostream>
#include <optional>
#include <ostream>
#include <string>
@@ -28,6 +34,8 @@
namespace iorap::db {
+const constexpr int kDbVersion = 2;
+
struct SqliteDbDeleter {
void operator()(sqlite3* db) {
if (db != nullptr) {
@@ -368,16 +376,34 @@
}
sqlite3* db = nullptr;
+ bool is_deprecated = false;
if (location != ":memory:") {
// Try to open DB if it already exists.
rc = sqlite3_open_v2(location.c_str(), /*out*/&db, SQLITE_OPEN_READWRITE, /*vfs*/nullptr);
if (rc == SQLITE_OK) {
LOG(INFO) << "Opened existing database at '" << location << "'";
- return SchemaModel{DbHandle{db}, location};
+ SchemaModel schema{DbHandle{db}, location};
+ if (schema.Version() == kDbVersion) {
+ return schema;
+ } else {
+ LOG(DEBUG) << "The version is old, reinit the db."
+ << " old version is "
+ << schema.Version()
+ << " and new version is "
+ << kDbVersion;
+ CleanUpFilesForDb(schema.db());
+ is_deprecated = true;
+ }
}
}
+ if (is_deprecated) {
+ // Remove the db and recreate it.
+ // TODO: migrate to a newer version without deleting the old one.
+ std::filesystem::remove(location.c_str());
+ }
+
// Create a new DB if one didn't exist already.
rc = sqlite3_open(location.c_str(), /*out*/&db);
@@ -452,12 +478,11 @@
CREATE TABLE schema_versions(
version INTEGER NOT NULL
);
- INSERT INTO schema_versions VALUES(1);
CREATE TABLE packages(
id INTEGER NOT NULL,
name TEXT NOT NULL,
- version INTEGER,
+ version INTEGER NOT NULL,
PRIMARY KEY(id)
);
@@ -468,7 +493,7 @@
package_id INTEGER NOT NULL,
PRIMARY KEY(id),
- FOREIGN KEY (package_id) REFERENCES packages (id)
+ FOREIGN KEY (package_id) REFERENCES packages (id) ON DELETE CASCADE
);
CREATE TABLE app_launch_histories(
@@ -485,7 +510,7 @@
-- absolute timestamp since epoch
report_fully_drawn_ns INTEGER CHECK(report_fully_drawn_ns IS NULL or report_fully_drawn_ns >= 0),
- FOREIGN KEY (activity_id) REFERENCES activities (id)
+ FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
);
CREATE TABLE raw_traces(
@@ -493,7 +518,7 @@
history_id INTEGER NOT NULL,
file_path TEXT NOT NULL,
- FOREIGN KEY (history_id) REFERENCES app_launch_histories (id)
+ FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE
);
CREATE TABLE prefetch_files(
@@ -501,7 +526,7 @@
activity_id INTEGER NOT NULL,
file_path TEXT NOT NULL,
- FOREIGN KEY (activity_id) REFERENCES activities (id)
+ FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
);
)SQLC0D3";
@@ -515,6 +540,21 @@
if (rc != SQLITE_OK) {
LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr";
}
+
+ const char* sql_to_insert_schema_version = R"SQLC0D3(
+ INSERT INTO schema_versions VALUES(%d)
+ )SQLC0D3";
+ rc = sqlite3_exec(db().get(),
+ android::String8::format(sql_to_insert_schema_version,
+ kDbVersion),
+ /*callback*/nullptr,
+ /*arg*/0,
+ /*out*/&err_msg);
+
+ if (rc != SQLITE_OK) {
+ LOG(FATAL) << "Failed to insert the schema version: "
+ << err_msg ? err_msg : "nullptr";
+ }
}
static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) {
@@ -559,6 +599,23 @@
return p;
}
+ static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db,
+ const char* name,
+ int version) {
+ ScopedLockDb lock{db};
+
+ std::string query =
+ "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;";
+ DbStatement stmt = DbStatement::Prepare(db, query, name, version);
+
+ PackageModel p{db};
+ if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
+ return std::nullopt;
+ }
+
+ return p;
+ }
+
static std::vector<PackageModel> SelectAll(DbHandle db) {
ScopedLockDb lock{db};
@@ -576,7 +633,7 @@
static std::optional<PackageModel> Insert(DbHandle db,
std::string name,
- std::optional<int> version) {
+ int version) {
const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
std::optional<int> inserted_row_id =
@@ -593,19 +650,21 @@
return p;
}
+ bool Delete() {
+ const char* sql = "DELETE FROM packages WHERE id = ?";
+
+ return DbQueryBuilder::Delete(db(), sql, id);
+ }
+
int id;
std::string name;
- std::optional<int> version;
+ int version;
};
inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
os << "version=";
- if (p.version) {
- os << *p.version;
- } else {
- os << "(nullopt)";
- }
+ os << p.version;
os << "}";
return os;
}
@@ -687,9 +746,11 @@
static std::optional<ActivityModel> SelectOrInsert(
DbHandle db,
std::string package_name,
- std::optional<int> package_version,
+ int package_version,
std::string activity_name) {
- std::optional<PackageModel> package = PackageModel::SelectByName(db, package_name.c_str());
+ std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db,
+ package_name.c_str(),
+ package_version);
if (!package) {
package = PackageModel::Insert(db, package_name, package_version);
DCHECK(package.has_value());
@@ -892,9 +953,8 @@
public:
// Return raw_traces, sorted ascending by the id.
- static std::vector<RawTraceModel> SelectByPackageNameActivityName(DbHandle db,
- std::string package_name,
- std::string activity_name) {
+ static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db,
+ VersionedComponentName vcn) {
ScopedLockDb lock{db};
const char* sql =
@@ -903,10 +963,14 @@
"INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
"INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
"INNER JOIN packages ON packages.id = activities.package_id "
- "WHERE packages.name = ? AND activities.name = ? "
+ "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"
"ORDER BY raw_traces.id ASC";
- DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
+ DbStatement stmt = DbStatement::Prepare(db,
+ sql,
+ vcn.GetPackage(),
+ vcn.GetActivity(),
+ vcn.GetVersion());
std::vector<RawTraceModel> results;
@@ -979,8 +1043,9 @@
}
public:
- static std::optional<PrefetchFileModel> SelectByPackageNameActivityName(
- DbHandle db, const std::string& package_name, const std::string& activity_name) {
+ static std::optional<PrefetchFileModel> SelectByVersionedComponentName(
+ DbHandle db,
+ VersionedComponentName vcn) {
ScopedLockDb lock{db};
const char* sql =
@@ -988,9 +1053,13 @@
"FROM prefetch_files "
"INNER JOIN activities ON activities.id = prefetch_files.activity_id "
"INNER JOIN packages ON packages.id = activities.package_id "
- "WHERE packages.name = ? AND activities.name = ? ";
+ "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?";
- DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
+ DbStatement stmt = DbStatement::Prepare(db,
+ sql,
+ vcn.GetPackage(),
+ vcn.GetActivity(),
+ vcn.GetVersion());
PrefetchFileModel p{db};