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 #ifndef IORAP_SRC_DB_MODELS_H_ 16 #define IORAP_SRC_DB_MODELS_H_ 17 18 #include "clean_up.h" 19 #include "file_models.h" 20 21 #include <android-base/logging.h> 22 #include <utils/String8.h> 23 24 #include <filesystem> 25 #include <iostream> 26 #include <optional> 27 #include <ostream> 28 #include <string> 29 #include <sstream> 30 #include <type_traits> 31 #include <vector> 32 33 #include <sqlite3.h> 34 35 namespace iorap::db { 36 37 const constexpr int kDbVersion = 2; 38 39 struct SqliteDbDeleter { operatorSqliteDbDeleter40 void operator()(sqlite3* db) { 41 if (db != nullptr) { 42 LOG(VERBOSE) << "sqlite3_close for: " << db; 43 sqlite3_close(db); 44 } 45 } 46 }; 47 48 class DbHandle { 49 public: 50 // Take over ownership of sqlite3 db. DbHandle(sqlite3 * db)51 explicit DbHandle(sqlite3* db) 52 : db_{std::shared_ptr<sqlite3>{db, SqliteDbDeleter{}}}, 53 mutex_{std::make_shared<std::mutex>()} { 54 } 55 get()56 sqlite3* get() { 57 return db_.get(); 58 } 59 60 operator sqlite3*() { 61 return db_.get(); 62 } 63 mutex()64 std::mutex& mutex() { 65 return *mutex_.get(); 66 } 67 68 private: 69 std::shared_ptr<sqlite3> db_; 70 std::shared_ptr<std::mutex> mutex_; 71 }; 72 73 class ScopedLockDb { 74 public: ScopedLockDb(std::mutex & mutex)75 ScopedLockDb(std::mutex& mutex) : mutex(mutex) { 76 mutex.lock(); 77 } 78 ScopedLockDb(DbHandle & handle)79 ScopedLockDb(DbHandle& handle) : ScopedLockDb(handle.mutex()) { 80 } 81 ~ScopedLockDb()82 ~ScopedLockDb() { 83 mutex.unlock(); 84 } 85 private: 86 std::mutex& mutex; 87 }; 88 89 class DbStatement { 90 public: 91 template <typename ... Args> Prepare(DbHandle db,const std::string & sql,Args &&...args)92 static DbStatement Prepare(DbHandle db, const std::string& sql, Args&&... args) { 93 return Prepare(db, sql.c_str(), std::forward<Args>(args)...); 94 } 95 96 template <typename ... Args> Prepare(DbHandle db,const char * sql,Args &&...args)97 static DbStatement Prepare(DbHandle db, const char* sql, Args&&... args) { 98 DCHECK(db.get() != nullptr); 99 DCHECK(sql != nullptr); 100 101 // LOG(VERBOSE) << "Prepare DB=" << db.get(); 102 103 sqlite3_stmt* stmt = nullptr; 104 int rc = sqlite3_prepare_v2(db.get(), sql, -1, /*out*/&stmt, nullptr); 105 106 DbStatement db_stmt{db, stmt}; 107 DCHECK(db_stmt.CheckOk(rc)) << sql; 108 db_stmt.BindAll(std::forward<Args>(args)...); 109 110 return db_stmt; 111 } 112 DbStatement(DbHandle db,sqlite3_stmt * stmt)113 DbStatement(DbHandle db, sqlite3_stmt* stmt) : db_(db), stmt_(stmt) { 114 } 115 get()116 sqlite3_stmt* get() { 117 return stmt_; 118 } 119 db()120 DbHandle db() { 121 return db_; 122 } 123 124 // Successive BindAll calls *do not* start back at the 0th bind position. 125 template <typename T, typename ... Args> BindAll(T && arg,Args &&...args)126 void BindAll(T&& arg, Args&&... args) { 127 Bind(std::forward<T>(arg)); 128 BindAll(std::forward<Args>(args)...); 129 } 130 BindAll()131 void BindAll() {} 132 133 template <typename T> Bind(const std::optional<T> & value)134 void Bind(const std::optional<T>& value) { 135 if (value) { 136 Bind(*value); 137 } else { 138 BindNull(); 139 } 140 } 141 Bind(bool value)142 void Bind(bool value) { 143 CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value)); 144 } 145 Bind(int value)146 void Bind(int value) { 147 CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value)); 148 } 149 Bind(uint64_t value)150 void Bind(uint64_t value) { 151 CheckOk(sqlite3_bind_int64(stmt_, bind_counter_++, static_cast<int64_t>(value))); 152 } 153 Bind(const char * value)154 void Bind(const char* value) { 155 if (value != nullptr) { 156 //sqlite3_bind_text(stmt_, /*index*/bind_counter_++, value, -1, SQLITE_STATIC); 157 CheckOk(sqlite3_bind_text(stmt_, /*index*/bind_counter_++, value, -1, SQLITE_TRANSIENT)); 158 } else { 159 BindNull(); 160 } 161 } 162 Bind(const std::string & value)163 void Bind(const std::string& value) { 164 Bind(value.c_str()); 165 } 166 167 template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> Bind(E value)168 void Bind(E value) { 169 Bind(static_cast<std::underlying_type_t<E>>(value)); 170 } 171 BindNull()172 void BindNull() { 173 CheckOk(sqlite3_bind_null(stmt_, bind_counter_++)); 174 } 175 Step()176 int Step() { 177 ++step_counter_; 178 return sqlite3_step(stmt_); 179 } 180 Step(int expected)181 bool Step(int expected) { 182 int rc = Step(); 183 if (rc != expected) { 184 LOG(ERROR) << "SQLite error: " << sqlite3_errmsg(db_.get()); 185 return false; 186 } 187 return true; 188 } 189 190 // Successive ColumnAll calls start at the 0th column again. 191 template <typename T, typename ... Args> ColumnAll(T & first,Args &...rest)192 void ColumnAll(T& first, Args&... rest) { 193 Column(first); 194 ColumnAll(rest...); 195 // Reset column counter back to 0 196 column_counter_ = 0; 197 } 198 ColumnAll()199 void ColumnAll() {} 200 201 template <typename T> Column(std::optional<T> & value)202 void Column(std::optional<T>& value) { 203 T tmp; 204 Column(/*out*/tmp); 205 206 if (!tmp) { // disambiguate 0 and NULL 207 const unsigned char* text = sqlite3_column_text(stmt_, column_counter_ - 1); 208 if (text == nullptr) { 209 value = std::nullopt; 210 return; 211 } 212 } 213 value = std::move(tmp); 214 } 215 216 template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> Column(E & value)217 void Column(E& value) { 218 std::underlying_type_t<E> tmp; 219 Column(/*out*/tmp); 220 value = static_cast<E>(tmp); 221 } 222 Column(bool & value)223 void Column(bool& value) { 224 value = sqlite3_column_int(stmt_, column_counter_++); 225 } 226 Column(int & value)227 void Column(int& value) { 228 value = sqlite3_column_int(stmt_, column_counter_++); 229 } 230 Column(uint64_t & value)231 void Column(uint64_t& value) { 232 value = static_cast<uint64_t>(sqlite3_column_int64(stmt_, column_counter_++)); 233 } 234 Column(std::string & value)235 void Column(std::string& value) { 236 const unsigned char* text = sqlite3_column_text(stmt_, column_counter_++); 237 238 DCHECK(text != nullptr) << "Column should be marked NOT NULL, otherwise use optional<string>"; 239 if (text == nullptr) { 240 LOG(ERROR) << "Got NULL back for column " << column_counter_-1 241 << "; is this column marked NOT NULL?"; 242 value = "(((null)))"; // Don't segfault, keep going. 243 return; 244 } 245 246 value = std::string{reinterpret_cast<const char*>(text)}; 247 } 248 ExpandedSql()249 const char* ExpandedSql() { 250 char* p = sqlite3_expanded_sql(stmt_); 251 if (p == nullptr) { 252 return "(nullptr)"; 253 } 254 return p; 255 } 256 Sql()257 const char* Sql() { 258 const char* p = sqlite3_sql(stmt_); 259 if (p == nullptr) { 260 return "(nullptr)"; 261 } 262 return p; 263 } 264 265 DbStatement(DbStatement && other)266 DbStatement(DbStatement&& other) 267 : db_{other.db_}, stmt_{other.stmt_}, bind_counter_{other.bind_counter_}, 268 step_counter_{other.step_counter_} { 269 other.db_ = DbHandle{nullptr}; 270 other.stmt_ = nullptr; 271 } 272 ~DbStatement()273 ~DbStatement() { 274 if (stmt_ != nullptr) { 275 DCHECK_GT(step_counter_, 0) << " forgot to call Step()?"; 276 sqlite3_finalize(stmt_); 277 } 278 } 279 280 private: 281 bool CheckOk(int rc, int expected = SQLITE_OK) { 282 if (rc != expected) { 283 LOG(ERROR) << "Got error for SQL query: '" << Sql() << "'" 284 << ", expanded: '" << ExpandedSql() << "'"; 285 LOG(ERROR) << "Failed SQLite api call (" << rc << "): " << sqlite3_errstr(rc); 286 } 287 return rc == expected; 288 } 289 290 DbHandle db_; 291 sqlite3_stmt* stmt_; 292 int bind_counter_ = 1; 293 int step_counter_ = 0; 294 int column_counter_ = 0; 295 }; 296 297 class DbQueryBuilder { 298 public: 299 // Returns the row ID that was inserted last. 300 template <typename... Args> Insert(DbHandle db,const std::string & sql,Args &&...args)301 static std::optional<int> Insert(DbHandle db, const std::string& sql, Args&&... args) { 302 ScopedLockDb lock{db}; 303 304 sqlite3_int64 last_rowid = sqlite3_last_insert_rowid(db.get()); 305 DbStatement stmt = DbStatement::Prepare(db, sql, std::forward<Args>(args)...); 306 307 if (!stmt.Step(SQLITE_DONE)) { 308 return std::nullopt; 309 } 310 311 last_rowid = sqlite3_last_insert_rowid(db.get()); 312 DCHECK_GT(last_rowid, 0); 313 314 return static_cast<int>(last_rowid); 315 } 316 317 template <typename... Args> Delete(DbHandle db,const std::string & sql,Args &&...args)318 static bool Delete(DbHandle db, const std::string& sql, Args&&... args) { 319 return ExecuteOnce(db, sql, std::forward<Args>(args)...); 320 } 321 322 template <typename... Args> Update(DbHandle db,const std::string & sql,Args &&...args)323 static bool Update(DbHandle db, const std::string& sql, Args&&... args) { 324 return ExecuteOnce(db, sql, std::forward<Args>(args)...); 325 } 326 327 template <typename... Args> ExecuteOnce(DbHandle db,const std::string & sql,Args &&...args)328 static bool ExecuteOnce(DbHandle db, const std::string& sql, Args&&... args) { 329 ScopedLockDb lock{db}; 330 331 DbStatement stmt = DbStatement::Prepare(db, sql, std::forward<Args>(args)...); 332 333 if (!stmt.Step(SQLITE_DONE)) { 334 return false; 335 } 336 337 return true; 338 } 339 340 template <typename... Args> SelectOnce(DbStatement & stmt,Args &...args)341 static bool SelectOnce(DbStatement& stmt, Args&... args) { 342 int rc = stmt.Step(); 343 switch (rc) { 344 case SQLITE_ROW: 345 stmt.ColumnAll(/*out*/args...); 346 return true; 347 case SQLITE_DONE: 348 return false; 349 default: 350 LOG(ERROR) << "Failed to step (" << rc << "): " << sqlite3_errmsg(stmt.db()); 351 return false; 352 } 353 } 354 }; 355 356 class Model { 357 public: db()358 DbHandle db() const { 359 return db_; 360 } 361 Model(DbHandle db)362 Model(DbHandle db) : db_{db} { 363 } 364 365 private: 366 DbHandle db_; 367 }; 368 369 class SchemaModel : public Model { 370 public: GetOrCreate(std::string location)371 static SchemaModel GetOrCreate(std::string location) { 372 int rc = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, /*data*/nullptr); 373 374 if (rc != SQLITE_OK) { 375 LOG(FATAL) << "Failed to configure logging"; 376 } 377 378 sqlite3* db = nullptr; 379 bool is_deprecated = false; 380 if (location != ":memory:") { 381 // Try to open DB if it already exists. 382 rc = sqlite3_open_v2(location.c_str(), /*out*/&db, SQLITE_OPEN_READWRITE, /*vfs*/nullptr); 383 384 if (rc == SQLITE_OK) { 385 LOG(INFO) << "Opened existing database at '" << location << "'"; 386 SchemaModel schema{DbHandle{db}, location}; 387 if (schema.Version() == kDbVersion) { 388 return schema; 389 } else { 390 LOG(DEBUG) << "The version is old, reinit the db." 391 << " old version is " 392 << schema.Version() 393 << " and new version is " 394 << kDbVersion; 395 CleanUpFilesForDb(schema.db()); 396 is_deprecated = true; 397 } 398 } 399 } 400 401 if (is_deprecated) { 402 // Remove the db and recreate it. 403 // TODO: migrate to a newer version without deleting the old one. 404 std::filesystem::remove(location.c_str()); 405 } 406 407 // Create a new DB if one didn't exist already. 408 rc = sqlite3_open(location.c_str(), /*out*/&db); 409 410 if (rc != SQLITE_OK) { 411 LOG(FATAL) << "Failed to open DB: " << sqlite3_errmsg(db); 412 } 413 414 SchemaModel schema{DbHandle{db}, location}; 415 schema.Reinitialize(); 416 // TODO: migrate versions upwards when we rev the schema version 417 418 int old_version = schema.Version(); 419 LOG(VERBOSE) << "Loaded schema version: " << old_version; 420 421 return schema; 422 } 423 MarkSingleton()424 void MarkSingleton() { 425 s_singleton_ = db(); 426 } 427 GetSingleton()428 static DbHandle GetSingleton() { 429 DCHECK(s_singleton_.has_value()); 430 return *s_singleton_; 431 } 432 Reinitialize()433 void Reinitialize() { 434 const char* sql_to_initialize = R"SQLC0D3( 435 DROP TABLE IF EXISTS schema_versions; 436 DROP TABLE IF EXISTS packages; 437 DROP TABLE IF EXISTS activities; 438 DROP TABLE IF EXISTS app_launch_histories; 439 DROP TABLE IF EXISTS raw_traces; 440 DROP TABLE IF EXISTS prefetch_files; 441 )SQLC0D3"; 442 char* err_msg = nullptr; 443 int rc = sqlite3_exec(db().get(), 444 sql_to_initialize, 445 /*callback*/nullptr, 446 /*arg*/0, 447 /*out*/&err_msg); 448 if (rc != SQLITE_OK) { 449 LOG(FATAL) << "Failed to drop tables: " << err_msg ? err_msg : "nullptr"; 450 } 451 452 CreateSchema(); 453 LOG(INFO) << "Reinitialized database at '" << location_ << "'"; 454 } 455 Version()456 int Version() { 457 std::string query = "SELECT MAX(version) FROM schema_versions;"; 458 DbStatement stmt = DbStatement::Prepare(db(), query); 459 460 int return_value = 0; 461 if (!DbQueryBuilder::SelectOnce(stmt, /*out*/return_value)) { 462 LOG(ERROR) << "Failed to query schema version"; 463 return -1; 464 } 465 466 return return_value; 467 } 468 469 protected: SchemaModel(DbHandle db,std::string location)470 SchemaModel(DbHandle db, std::string location) : Model{db}, location_(location) { 471 } 472 473 private: 474 static std::optional<DbHandle> s_singleton_; 475 CreateSchema()476 void CreateSchema() { 477 const char* sql_to_initialize = R"SQLC0D3( 478 CREATE TABLE schema_versions( 479 version INTEGER NOT NULL 480 ); 481 482 CREATE TABLE packages( 483 id INTEGER NOT NULL, 484 name TEXT NOT NULL, 485 version INTEGER NOT NULL, 486 487 PRIMARY KEY(id) 488 ); 489 490 CREATE TABLE activities( 491 id INTEGER NOT NULL, 492 name TEXT NOT NULL, 493 package_id INTEGER NOT NULL, 494 495 PRIMARY KEY(id), 496 FOREIGN KEY (package_id) REFERENCES packages (id) ON DELETE CASCADE 497 ); 498 499 CREATE TABLE app_launch_histories( 500 id INTEGER NOT NULL PRIMARY KEY, 501 activity_id INTEGER NOT NULL, 502 -- 1:Cold, 2:Warm, 3:Hot 503 temperature INTEGER CHECK (temperature IN (1, 2, 3)) NOT NULL, 504 trace_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL, 505 readahead_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL, 506 -- absolute timestamp since epoch 507 intent_started_ns INTEGER CHECK(intent_started_ns IS NULL or intent_started_ns >= 0), 508 -- absolute timestamp since epoch 509 total_time_ns INTEGER CHECK(total_time_ns IS NULL or total_time_ns >= 0), 510 -- absolute timestamp since epoch 511 report_fully_drawn_ns INTEGER CHECK(report_fully_drawn_ns IS NULL or report_fully_drawn_ns >= 0), 512 513 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE 514 ); 515 516 CREATE TABLE raw_traces( 517 id INTEGER NOT NULL PRIMARY KEY, 518 history_id INTEGER NOT NULL, 519 file_path TEXT NOT NULL, 520 521 FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE 522 ); 523 524 CREATE TABLE prefetch_files( 525 id INTEGER NOT NULL PRIMARY KEY, 526 activity_id INTEGER NOT NULL, 527 file_path TEXT NOT NULL, 528 529 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE 530 ); 531 )SQLC0D3"; 532 533 char* err_msg = nullptr; 534 int rc = sqlite3_exec(db().get(), 535 sql_to_initialize, 536 /*callback*/nullptr, 537 /*arg*/0, 538 /*out*/&err_msg); 539 540 if (rc != SQLITE_OK) { 541 LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr"; 542 } 543 544 const char* sql_to_insert_schema_version = R"SQLC0D3( 545 INSERT INTO schema_versions VALUES(%d) 546 )SQLC0D3"; 547 rc = sqlite3_exec(db().get(), 548 android::String8::format(sql_to_insert_schema_version, 549 kDbVersion), 550 /*callback*/nullptr, 551 /*arg*/0, 552 /*out*/&err_msg); 553 554 if (rc != SQLITE_OK) { 555 LOG(FATAL) << "Failed to insert the schema version: " 556 << err_msg ? err_msg : "nullptr"; 557 } 558 } 559 ErrorLogCallback(void * pArg,int iErrCode,const char * zMsg)560 static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) { 561 LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg; 562 } 563 564 std::string location_; 565 }; 566 567 class PackageModel : public Model { 568 protected: PackageModel(DbHandle db)569 PackageModel(DbHandle db) : Model{db} { 570 } 571 572 public: SelectById(DbHandle db,int id)573 static std::optional<PackageModel> SelectById(DbHandle db, int id) { 574 ScopedLockDb lock{db}; 575 int original_id = id; 576 577 std::string query = "SELECT * FROM packages WHERE id = ?1 LIMIT 1;"; 578 DbStatement stmt = DbStatement::Prepare(db, query, id); 579 580 PackageModel p{db}; 581 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 582 return std::nullopt; 583 } 584 585 return p; 586 } 587 SelectByName(DbHandle db,const char * name)588 static std::vector<PackageModel> SelectByName(DbHandle db, const char* name) { 589 ScopedLockDb lock{db}; 590 591 std::string query = "SELECT * FROM packages WHERE name = ?1;"; 592 DbStatement stmt = DbStatement::Prepare(db, query, name); 593 594 std::vector<PackageModel> packages; 595 596 PackageModel p{db}; 597 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 598 packages.push_back(p); 599 } 600 601 return packages; 602 } 603 SelectByNameAndVersion(DbHandle db,const char * name,int version)604 static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db, 605 const char* name, 606 int version) { 607 ScopedLockDb lock{db}; 608 609 std::string query = 610 "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;"; 611 DbStatement stmt = DbStatement::Prepare(db, query, name, version); 612 613 PackageModel p{db}; 614 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 615 return std::nullopt; 616 } 617 618 return p; 619 } 620 SelectAll(DbHandle db)621 static std::vector<PackageModel> SelectAll(DbHandle db) { 622 ScopedLockDb lock{db}; 623 624 std::string query = "SELECT * FROM packages;"; 625 DbStatement stmt = DbStatement::Prepare(db, query); 626 627 std::vector<PackageModel> packages; 628 PackageModel p{db}; 629 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 630 packages.push_back(p); 631 } 632 633 return packages; 634 } 635 Insert(DbHandle db,std::string name,int version)636 static std::optional<PackageModel> Insert(DbHandle db, 637 std::string name, 638 int version) { 639 const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);"; 640 641 std::optional<int> inserted_row_id = 642 DbQueryBuilder::Insert(db, sql, name, version); 643 if (!inserted_row_id) { 644 return std::nullopt; 645 } 646 647 PackageModel p{db}; 648 p.name = name; 649 p.version = version; 650 p.id = *inserted_row_id; 651 652 return p; 653 } 654 Delete()655 bool Delete() { 656 const char* sql = "DELETE FROM packages WHERE id = ?"; 657 658 return DbQueryBuilder::Delete(db(), sql, id); 659 } 660 661 int id; 662 std::string name; 663 int version; 664 }; 665 666 inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) { 667 os << "PackageModel{id=" << p.id << ",name=" << p.name << ","; 668 os << "version="; 669 os << p.version; 670 os << "}"; 671 return os; 672 } 673 674 class ActivityModel : public Model { 675 protected: ActivityModel(DbHandle db)676 ActivityModel(DbHandle db) : Model{db} { 677 } 678 679 public: SelectById(DbHandle db,int id)680 static std::optional<ActivityModel> SelectById(DbHandle db, int id) { 681 ScopedLockDb lock{db}; 682 int original_id = id; 683 684 std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;"; 685 DbStatement stmt = DbStatement::Prepare(db, query, id); 686 687 ActivityModel p{db}; 688 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 689 return std::nullopt; 690 } 691 692 return p; 693 } 694 SelectByNameAndPackageId(DbHandle db,const char * name,int package_id)695 static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db, 696 const char* name, 697 int package_id) { 698 ScopedLockDb lock{db}; 699 700 std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;"; 701 DbStatement stmt = DbStatement::Prepare(db, query, name, package_id); 702 703 ActivityModel p{db}; 704 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 705 return std::nullopt; 706 } 707 708 return p; 709 } 710 SelectByPackageId(DbHandle db,int package_id)711 static std::vector<ActivityModel> SelectByPackageId(DbHandle db, 712 int package_id) { 713 ScopedLockDb lock{db}; 714 715 std::string query = "SELECT * FROM activities WHERE package_id = ?;"; 716 DbStatement stmt = DbStatement::Prepare(db, query, package_id); 717 718 std::vector<ActivityModel> activities; 719 ActivityModel p{db}; 720 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 721 activities.push_back(p); 722 } 723 724 return activities; 725 } 726 Insert(DbHandle db,std::string name,int package_id)727 static std::optional<ActivityModel> Insert(DbHandle db, 728 std::string name, 729 int package_id) { 730 const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);"; 731 732 std::optional<int> inserted_row_id = 733 DbQueryBuilder::Insert(db, sql, name, package_id); 734 if (!inserted_row_id) { 735 return std::nullopt; 736 } 737 738 ActivityModel p{db}; 739 p.id = *inserted_row_id; 740 p.name = name; 741 p.package_id = package_id; 742 743 return p; 744 } 745 746 // Try to select by package_name+activity_name, otherwise insert into both tables. 747 // Package version is ignored for selects. SelectOrInsert(DbHandle db,std::string package_name,int package_version,std::string activity_name)748 static std::optional<ActivityModel> SelectOrInsert( 749 DbHandle db, 750 std::string package_name, 751 int package_version, 752 std::string activity_name) { 753 std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db, 754 package_name.c_str(), 755 package_version); 756 if (!package) { 757 package = PackageModel::Insert(db, package_name, package_version); 758 DCHECK(package.has_value()); 759 } 760 761 std::optional<ActivityModel> activity = 762 ActivityModel::SelectByNameAndPackageId(db, 763 activity_name.c_str(), 764 package->id); 765 if (!activity) { 766 activity = Insert(db, activity_name, package->id); 767 // XX: should we really return an optional here? This feels like it should never fail. 768 } 769 770 return activity; 771 } 772 773 int id; 774 std::string name; 775 int package_id; // PackageModel::id 776 }; 777 778 inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) { 779 os << "ActivityModel{id=" << p.id << ",name=" << p.name << ","; 780 os << "package_id=" << p.package_id << "}"; 781 return os; 782 } 783 784 class AppLaunchHistoryModel : public Model { 785 protected: AppLaunchHistoryModel(DbHandle db)786 AppLaunchHistoryModel(DbHandle db) : Model{db} { 787 } 788 789 public: 790 enum class Temperature : int32_t { 791 kUninitialized = -1, // Note: Not a valid SQL value. 792 kCold = 1, 793 kWarm = 2, 794 kHot = 3, 795 }; 796 SelectById(DbHandle db,int id)797 static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) { 798 ScopedLockDb lock{db}; 799 int original_id = id; 800 801 std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;"; 802 DbStatement stmt = DbStatement::Prepare(db, query, id); 803 804 AppLaunchHistoryModel p{db}; 805 if (!DbQueryBuilder::SelectOnce(stmt, 806 p.id, 807 p.activity_id, 808 p.temperature, 809 p.trace_enabled, 810 p.readahead_enabled, 811 p.intent_started_ns, 812 p.total_time_ns, 813 p.report_fully_drawn_ns)) { 814 return std::nullopt; 815 } 816 817 return p; 818 } 819 820 // Selects the activity history for an activity id. 821 // The requirements are: 822 // * Should be cold run. 823 // * Pefetto trace is enabled. 824 // * intent_start_ns is *NOT* null. SelectActivityHistoryForCompile(DbHandle db,int activity_id)825 static std::vector<AppLaunchHistoryModel> SelectActivityHistoryForCompile( 826 DbHandle db, 827 int activity_id) { 828 ScopedLockDb lock{db}; 829 std::string query = "SELECT * FROM app_launch_histories " 830 "WHERE activity_id = ?1 AND" 831 " temperature = 1 AND" 832 " trace_enabled = TRUE AND" 833 " intent_started_ns IS NOT NULL;"; 834 DbStatement stmt = DbStatement::Prepare(db, query, activity_id); 835 std::vector<AppLaunchHistoryModel> result; 836 837 AppLaunchHistoryModel p{db}; 838 while (DbQueryBuilder::SelectOnce(stmt, 839 p.id, 840 p.activity_id, 841 p.temperature, 842 p.trace_enabled, 843 p.readahead_enabled, 844 p.intent_started_ns, 845 p.total_time_ns, 846 p.report_fully_drawn_ns)) { 847 result.push_back(p); 848 } 849 return result; 850 } 851 Insert(DbHandle db,int activity_id,AppLaunchHistoryModel::Temperature temperature,bool trace_enabled,bool readahead_enabled,std::optional<uint64_t> intent_started_ns,std::optional<uint64_t> total_time_ns,std::optional<uint64_t> report_fully_drawn_ns)852 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db, 853 int activity_id, 854 AppLaunchHistoryModel::Temperature temperature, 855 bool trace_enabled, 856 bool readahead_enabled, 857 std::optional<uint64_t> intent_started_ns, 858 std::optional<uint64_t> total_time_ns, 859 std::optional<uint64_t> report_fully_drawn_ns) 860 { 861 const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, " 862 "readahead_enabled, intent_started_ns, " 863 "total_time_ns, report_fully_drawn_ns) " 864 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);"; 865 866 std::optional<int> inserted_row_id = 867 DbQueryBuilder::Insert(db, 868 sql, 869 activity_id, 870 temperature, 871 trace_enabled, 872 readahead_enabled, 873 intent_started_ns, 874 total_time_ns, 875 report_fully_drawn_ns); 876 if (!inserted_row_id) { 877 return std::nullopt; 878 } 879 880 AppLaunchHistoryModel p{db}; 881 p.id = *inserted_row_id; 882 p.activity_id = activity_id; 883 p.temperature = temperature; 884 p.trace_enabled = trace_enabled; 885 p.readahead_enabled = readahead_enabled; 886 p.intent_started_ns = intent_started_ns; 887 p.total_time_ns = total_time_ns; 888 p.report_fully_drawn_ns = report_fully_drawn_ns; 889 890 return p; 891 } 892 UpdateReportFullyDrawn(DbHandle db,int history_id,uint64_t report_fully_drawn_ns)893 static bool UpdateReportFullyDrawn(DbHandle db, 894 int history_id, 895 uint64_t report_fully_drawn_ns) 896 { 897 const char* sql = "UPDATE app_launch_histories " 898 "SET report_fully_drawn_ns = ?1 " 899 "WHERE id = ?2;"; 900 901 bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id); 902 903 if (!result) { 904 LOG(ERROR)<< "Failed to update history_id:"<< history_id 905 << ", report_fully_drawn_ns: " << report_fully_drawn_ns; 906 } 907 return result; 908 } 909 910 int id; 911 int activity_id; // ActivityModel::id 912 Temperature temperature = Temperature::kUninitialized; 913 bool trace_enabled; 914 bool readahead_enabled; 915 std::optional<uint64_t> intent_started_ns; 916 std::optional<uint64_t> total_time_ns; 917 std::optional<uint64_t> report_fully_drawn_ns; 918 }; 919 920 inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) { 921 os << "AppLaunchHistoryModel{id=" << p.id << "," 922 << "activity_id=" << p.activity_id << "," 923 << "temperature=" << static_cast<int>(p.temperature) << "," 924 << "trace_enabled=" << p.trace_enabled << "," 925 << "readahead_enabled=" << p.readahead_enabled << "," 926 << "intent_started_ns="; 927 if (p.intent_started_ns) { 928 os << *p.intent_started_ns; 929 } else { 930 os << "(nullopt)"; 931 } 932 os << ","; 933 os << "total_time_ns="; 934 if (p.total_time_ns) { 935 os << *p.total_time_ns; 936 } else { 937 os << "(nullopt)"; 938 } 939 os << ","; 940 os << "report_fully_drawn_ns="; 941 if (p.report_fully_drawn_ns) { 942 os << *p.report_fully_drawn_ns; 943 } else { 944 os << "(nullopt)"; 945 } 946 os << "}"; 947 return os; 948 } 949 950 class RawTraceModel : public Model { 951 protected: RawTraceModel(DbHandle db)952 RawTraceModel(DbHandle db) : Model{db} { 953 } 954 955 public: 956 957 // Return raw_traces, sorted ascending by the id. SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)958 static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db, 959 VersionedComponentName vcn) { 960 ScopedLockDb lock{db}; 961 962 const char* sql = 963 "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path " 964 "FROM raw_traces " 965 "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id " 966 "INNER JOIN activities ON activities.id = app_launch_histories.activity_id " 967 "INNER JOIN packages ON packages.id = activities.package_id " 968 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?" 969 "ORDER BY raw_traces.id ASC"; 970 971 DbStatement stmt = DbStatement::Prepare(db, 972 sql, 973 vcn.GetPackage(), 974 vcn.GetActivity(), 975 vcn.GetVersion()); 976 977 std::vector<RawTraceModel> results; 978 979 RawTraceModel p{db}; 980 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) { 981 results.push_back(p); 982 } 983 984 return results; 985 } 986 SelectByHistoryId(DbHandle db,int history_id)987 static std::optional<RawTraceModel> SelectByHistoryId(DbHandle db, int history_id) { 988 ScopedLockDb lock{db}; 989 990 const char* sql = 991 "SELECT id, history_id, file_path " 992 "FROM raw_traces " 993 "WHERE history_id = ?1 " 994 "LIMIT 1;"; 995 996 DbStatement stmt = DbStatement::Prepare(db, sql, history_id); 997 998 RawTraceModel p{db}; 999 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) { 1000 return std::nullopt; 1001 } 1002 1003 return p; 1004 } 1005 Insert(DbHandle db,int history_id,std::string file_path)1006 static std::optional<RawTraceModel> Insert(DbHandle db, 1007 int history_id, 1008 std::string file_path) { 1009 const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);"; 1010 1011 std::optional<int> inserted_row_id = 1012 DbQueryBuilder::Insert(db, sql, history_id, file_path); 1013 if (!inserted_row_id) { 1014 return std::nullopt; 1015 } 1016 1017 RawTraceModel p{db}; 1018 p.id = *inserted_row_id; 1019 p.history_id = history_id; 1020 p.file_path = file_path; 1021 1022 return p; 1023 } 1024 Delete()1025 bool Delete() { 1026 const char* sql = "DELETE FROM raw_traces WHERE id = ?"; 1027 1028 return DbQueryBuilder::Delete(db(), sql, id); 1029 } 1030 1031 int id; 1032 int history_id; 1033 std::string file_path; 1034 }; 1035 1036 inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) { 1037 os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ","; 1038 os << "file_path=" << p.file_path << "}"; 1039 return os; 1040 } 1041 1042 class PrefetchFileModel : public Model { 1043 protected: PrefetchFileModel(DbHandle db)1044 PrefetchFileModel(DbHandle db) : Model{db} { 1045 } 1046 1047 public: SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)1048 static std::optional<PrefetchFileModel> SelectByVersionedComponentName( 1049 DbHandle db, 1050 VersionedComponentName vcn) { 1051 ScopedLockDb lock{db}; 1052 1053 const char* sql = 1054 "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path " 1055 "FROM prefetch_files " 1056 "INNER JOIN activities ON activities.id = prefetch_files.activity_id " 1057 "INNER JOIN packages ON packages.id = activities.package_id " 1058 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"; 1059 1060 DbStatement stmt = DbStatement::Prepare(db, 1061 sql, 1062 vcn.GetPackage(), 1063 vcn.GetActivity(), 1064 vcn.GetVersion()); 1065 1066 PrefetchFileModel p{db}; 1067 1068 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) { 1069 return std::nullopt; 1070 } 1071 1072 return p; 1073 } 1074 SelectAll(DbHandle db)1075 static std::vector<PrefetchFileModel> SelectAll(DbHandle db) { 1076 ScopedLockDb lock{db}; 1077 1078 std::string query = 1079 "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path " 1080 "FROM prefetch_files"; 1081 DbStatement stmt = DbStatement::Prepare(db, query); 1082 1083 std::vector<PrefetchFileModel> prefetch_files; 1084 PrefetchFileModel p{db}; 1085 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) { 1086 prefetch_files.push_back(p); 1087 } 1088 1089 return prefetch_files; 1090 } 1091 Insert(DbHandle db,int activity_id,std::string file_path)1092 static std::optional<PrefetchFileModel> Insert(DbHandle db, 1093 int activity_id, 1094 std::string file_path) { 1095 const char* sql = "INSERT INTO prefetch_files (activity_id, file_path) VALUES (?1, ?2);"; 1096 1097 std::optional<int> inserted_row_id = 1098 DbQueryBuilder::Insert(db, sql, activity_id, file_path); 1099 if (!inserted_row_id) { 1100 return std::nullopt; 1101 } 1102 1103 PrefetchFileModel p{db}; 1104 p.id = *inserted_row_id; 1105 p.activity_id = activity_id; 1106 p.file_path = file_path; 1107 1108 return p; 1109 } 1110 Delete()1111 bool Delete() { 1112 const char* sql = "DELETE FROM prefetch_files WHERE id = ?"; 1113 1114 return DbQueryBuilder::Delete(db(), sql, id); 1115 } 1116 1117 int id; 1118 int activity_id; 1119 std::string file_path; 1120 }; 1121 1122 inline std::ostream& operator<<(std::ostream& os, const PrefetchFileModel& p) { 1123 os << "PrefetchFileModel{id=" << p.id << ",activity_id=" << p.activity_id << ","; 1124 os << "file_path=" << p.file_path << "}"; 1125 return os; 1126 } 1127 1128 } // namespace iorap::db 1129 1130 #endif // IORAP_SRC_DB_MODELS_H_ 1131