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 = 3; 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 -- pid of the app 513 pid INTEGER CHECK(pid IS NULL or pid >= 0), 514 515 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE 516 ); 517 518 CREATE TABLE raw_traces( 519 id INTEGER NOT NULL PRIMARY KEY, 520 history_id INTEGER NOT NULL, 521 file_path TEXT NOT NULL, 522 523 FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE 524 ); 525 526 CREATE TABLE prefetch_files( 527 id INTEGER NOT NULL PRIMARY KEY, 528 activity_id INTEGER NOT NULL, 529 file_path TEXT NOT NULL, 530 531 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE 532 ); 533 )SQLC0D3"; 534 535 char* err_msg = nullptr; 536 int rc = sqlite3_exec(db().get(), 537 sql_to_initialize, 538 /*callback*/nullptr, 539 /*arg*/0, 540 /*out*/&err_msg); 541 542 if (rc != SQLITE_OK) { 543 LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr"; 544 } 545 546 const char* sql_to_insert_schema_version = R"SQLC0D3( 547 INSERT INTO schema_versions VALUES(%d) 548 )SQLC0D3"; 549 rc = sqlite3_exec(db().get(), 550 android::String8::format(sql_to_insert_schema_version, 551 kDbVersion), 552 /*callback*/nullptr, 553 /*arg*/0, 554 /*out*/&err_msg); 555 556 if (rc != SQLITE_OK) { 557 LOG(FATAL) << "Failed to insert the schema version: " 558 << err_msg ? err_msg : "nullptr"; 559 } 560 } 561 ErrorLogCallback(void * pArg,int iErrCode,const char * zMsg)562 static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) { 563 LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg; 564 } 565 566 std::string location_; 567 }; 568 569 class PackageModel : public Model { 570 protected: PackageModel(DbHandle db)571 PackageModel(DbHandle db) : Model{db} { 572 } 573 574 public: SelectById(DbHandle db,int id)575 static std::optional<PackageModel> SelectById(DbHandle db, int id) { 576 ScopedLockDb lock{db}; 577 int original_id = id; 578 579 std::string query = "SELECT * FROM packages WHERE id = ?1 LIMIT 1;"; 580 DbStatement stmt = DbStatement::Prepare(db, query, id); 581 582 PackageModel p{db}; 583 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 584 return std::nullopt; 585 } 586 587 return p; 588 } 589 SelectByName(DbHandle db,const char * name)590 static std::vector<PackageModel> SelectByName(DbHandle db, const char* name) { 591 ScopedLockDb lock{db}; 592 593 std::string query = "SELECT * FROM packages WHERE name = ?1;"; 594 DbStatement stmt = DbStatement::Prepare(db, query, name); 595 596 std::vector<PackageModel> packages; 597 598 PackageModel p{db}; 599 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 600 packages.push_back(p); 601 } 602 603 return packages; 604 } 605 SelectByNameAndVersion(DbHandle db,const char * name,int version)606 static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db, 607 const char* name, 608 int version) { 609 ScopedLockDb lock{db}; 610 611 std::string query = 612 "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;"; 613 DbStatement stmt = DbStatement::Prepare(db, query, name, version); 614 615 PackageModel p{db}; 616 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 617 return std::nullopt; 618 } 619 620 return p; 621 } 622 SelectAll(DbHandle db)623 static std::vector<PackageModel> SelectAll(DbHandle db) { 624 ScopedLockDb lock{db}; 625 626 std::string query = "SELECT * FROM packages;"; 627 DbStatement stmt = DbStatement::Prepare(db, query); 628 629 std::vector<PackageModel> packages; 630 PackageModel p{db}; 631 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) { 632 packages.push_back(p); 633 } 634 635 return packages; 636 } 637 Insert(DbHandle db,std::string name,int version)638 static std::optional<PackageModel> Insert(DbHandle db, 639 std::string name, 640 int version) { 641 const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);"; 642 643 std::optional<int> inserted_row_id = 644 DbQueryBuilder::Insert(db, sql, name, version); 645 if (!inserted_row_id) { 646 return std::nullopt; 647 } 648 649 PackageModel p{db}; 650 p.name = name; 651 p.version = version; 652 p.id = *inserted_row_id; 653 654 return p; 655 } 656 Delete()657 bool Delete() { 658 const char* sql = "DELETE FROM packages WHERE id = ?"; 659 660 return DbQueryBuilder::Delete(db(), sql, id); 661 } 662 663 int id; 664 std::string name; 665 int version; 666 }; 667 668 inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) { 669 os << "PackageModel{id=" << p.id << ",name=" << p.name << ","; 670 os << "version="; 671 os << p.version; 672 os << "}"; 673 return os; 674 } 675 676 class ActivityModel : public Model { 677 protected: ActivityModel(DbHandle db)678 ActivityModel(DbHandle db) : Model{db} { 679 } 680 681 public: SelectById(DbHandle db,int id)682 static std::optional<ActivityModel> SelectById(DbHandle db, int id) { 683 ScopedLockDb lock{db}; 684 int original_id = id; 685 686 std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;"; 687 DbStatement stmt = DbStatement::Prepare(db, query, id); 688 689 ActivityModel p{db}; 690 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 691 return std::nullopt; 692 } 693 694 return p; 695 } 696 SelectByNameAndPackageId(DbHandle db,const char * name,int package_id)697 static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db, 698 const char* name, 699 int package_id) { 700 ScopedLockDb lock{db}; 701 702 std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;"; 703 DbStatement stmt = DbStatement::Prepare(db, query, name, package_id); 704 705 ActivityModel p{db}; 706 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 707 return std::nullopt; 708 } 709 710 return p; 711 } 712 SelectByPackageId(DbHandle db,int package_id)713 static std::vector<ActivityModel> SelectByPackageId(DbHandle db, 714 int package_id) { 715 ScopedLockDb lock{db}; 716 717 std::string query = "SELECT * FROM activities WHERE package_id = ?;"; 718 DbStatement stmt = DbStatement::Prepare(db, query, package_id); 719 720 std::vector<ActivityModel> activities; 721 ActivityModel p{db}; 722 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) { 723 activities.push_back(p); 724 } 725 726 return activities; 727 } 728 Insert(DbHandle db,std::string name,int package_id)729 static std::optional<ActivityModel> Insert(DbHandle db, 730 std::string name, 731 int package_id) { 732 const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);"; 733 734 std::optional<int> inserted_row_id = 735 DbQueryBuilder::Insert(db, sql, name, package_id); 736 if (!inserted_row_id) { 737 return std::nullopt; 738 } 739 740 ActivityModel p{db}; 741 p.id = *inserted_row_id; 742 p.name = name; 743 p.package_id = package_id; 744 745 return p; 746 } 747 748 // Try to select by package_name+activity_name, otherwise insert into both tables. 749 // Package version is ignored for selects. SelectOrInsert(DbHandle db,std::string package_name,int package_version,std::string activity_name)750 static std::optional<ActivityModel> SelectOrInsert( 751 DbHandle db, 752 std::string package_name, 753 int package_version, 754 std::string activity_name) { 755 std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db, 756 package_name.c_str(), 757 package_version); 758 if (!package) { 759 package = PackageModel::Insert(db, package_name, package_version); 760 DCHECK(package.has_value()); 761 } 762 763 std::optional<ActivityModel> activity = 764 ActivityModel::SelectByNameAndPackageId(db, 765 activity_name.c_str(), 766 package->id); 767 if (!activity) { 768 activity = Insert(db, activity_name, package->id); 769 // XX: should we really return an optional here? This feels like it should never fail. 770 } 771 772 return activity; 773 } 774 775 int id; 776 std::string name; 777 int package_id; // PackageModel::id 778 }; 779 780 inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) { 781 os << "ActivityModel{id=" << p.id << ",name=" << p.name << ","; 782 os << "package_id=" << p.package_id << "}"; 783 return os; 784 } 785 786 class AppLaunchHistoryModel : public Model { 787 protected: AppLaunchHistoryModel(DbHandle db)788 AppLaunchHistoryModel(DbHandle db) : Model{db} { 789 } 790 791 public: 792 enum class Temperature : int32_t { 793 kUninitialized = -1, // Note: Not a valid SQL value. 794 kCold = 1, 795 kWarm = 2, 796 kHot = 3, 797 }; 798 SelectById(DbHandle db,int id)799 static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) { 800 ScopedLockDb lock{db}; 801 int original_id = id; 802 803 std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;"; 804 DbStatement stmt = DbStatement::Prepare(db, query, id); 805 806 AppLaunchHistoryModel p{db}; 807 if (!DbQueryBuilder::SelectOnce(stmt, 808 p.id, 809 p.activity_id, 810 p.temperature, 811 p.trace_enabled, 812 p.readahead_enabled, 813 p.intent_started_ns, 814 p.total_time_ns, 815 p.report_fully_drawn_ns, 816 p.pid)) { 817 return std::nullopt; 818 } 819 820 return p; 821 } 822 823 // Selects the activity history for an activity id. 824 // The requirements are: 825 // * Should be cold run. 826 // * Pefetto trace is enabled. 827 // * intent_start_ns is *NOT* null. SelectActivityHistoryForCompile(DbHandle db,int activity_id)828 static std::vector<AppLaunchHistoryModel> SelectActivityHistoryForCompile( 829 DbHandle db, 830 int activity_id) { 831 ScopedLockDb lock{db}; 832 std::string query = "SELECT * FROM app_launch_histories " 833 "WHERE activity_id = ?1 AND" 834 " temperature = 1 AND" 835 " trace_enabled = TRUE AND" 836 " intent_started_ns IS NOT NULL;"; 837 DbStatement stmt = DbStatement::Prepare(db, query, activity_id); 838 std::vector<AppLaunchHistoryModel> result; 839 840 AppLaunchHistoryModel p{db}; 841 while (DbQueryBuilder::SelectOnce(stmt, 842 p.id, 843 p.activity_id, 844 p.temperature, 845 p.trace_enabled, 846 p.readahead_enabled, 847 p.intent_started_ns, 848 p.total_time_ns, 849 p.report_fully_drawn_ns, 850 p.pid)) { 851 result.push_back(p); 852 } 853 return result; 854 } 855 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,int32_t pid)856 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db, 857 int activity_id, 858 AppLaunchHistoryModel::Temperature temperature, 859 bool trace_enabled, 860 bool readahead_enabled, 861 std::optional<uint64_t> intent_started_ns, 862 std::optional<uint64_t> total_time_ns, 863 std::optional<uint64_t> report_fully_drawn_ns, 864 int32_t pid) 865 { 866 const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, " 867 "readahead_enabled, intent_started_ns, " 868 "total_time_ns, report_fully_drawn_ns, pid) " 869 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);"; 870 871 std::optional<int> inserted_row_id = 872 DbQueryBuilder::Insert(db, 873 sql, 874 activity_id, 875 temperature, 876 trace_enabled, 877 readahead_enabled, 878 intent_started_ns, 879 total_time_ns, 880 report_fully_drawn_ns, 881 pid); 882 if (!inserted_row_id) { 883 return std::nullopt; 884 } 885 886 AppLaunchHistoryModel p{db}; 887 p.id = *inserted_row_id; 888 p.activity_id = activity_id; 889 p.temperature = temperature; 890 p.trace_enabled = trace_enabled; 891 p.readahead_enabled = readahead_enabled; 892 p.intent_started_ns = intent_started_ns; 893 p.total_time_ns = total_time_ns; 894 p.report_fully_drawn_ns = report_fully_drawn_ns; 895 p.pid = pid; 896 897 return p; 898 } 899 UpdateReportFullyDrawn(DbHandle db,int history_id,uint64_t report_fully_drawn_ns)900 static bool UpdateReportFullyDrawn(DbHandle db, 901 int history_id, 902 uint64_t report_fully_drawn_ns) 903 { 904 const char* sql = "UPDATE app_launch_histories " 905 "SET report_fully_drawn_ns = ?1 " 906 "WHERE id = ?2;"; 907 908 bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id); 909 910 if (!result) { 911 LOG(ERROR)<< "Failed to update history_id:"<< history_id 912 << ", report_fully_drawn_ns: " << report_fully_drawn_ns; 913 } 914 return result; 915 } 916 917 int id; 918 int activity_id; // ActivityModel::id 919 Temperature temperature = Temperature::kUninitialized; 920 bool trace_enabled; 921 bool readahead_enabled; 922 std::optional<uint64_t> intent_started_ns; 923 std::optional<uint64_t> total_time_ns; 924 std::optional<uint64_t> report_fully_drawn_ns; 925 int32_t pid; 926 }; 927 928 inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) { 929 os << "AppLaunchHistoryModel{id=" << p.id << "," 930 << "activity_id=" << p.activity_id << "," 931 << "temperature=" << static_cast<int>(p.temperature) << "," 932 << "trace_enabled=" << p.trace_enabled << "," 933 << "readahead_enabled=" << p.readahead_enabled << "," 934 << "intent_started_ns="; 935 if (p.intent_started_ns) { 936 os << *p.intent_started_ns; 937 } else { 938 os << "(nullopt)"; 939 } 940 os << ","; 941 os << "total_time_ns="; 942 if (p.total_time_ns) { 943 os << *p.total_time_ns; 944 } else { 945 os << "(nullopt)"; 946 } 947 os << ","; 948 os << "report_fully_drawn_ns="; 949 if (p.report_fully_drawn_ns) { 950 os << *p.report_fully_drawn_ns; 951 } else { 952 os << "(nullopt)"; 953 } 954 os << ","; 955 os << "pid=" << p.pid; 956 os << "}"; 957 return os; 958 } 959 960 class RawTraceModel : public Model { 961 protected: RawTraceModel(DbHandle db)962 RawTraceModel(DbHandle db) : Model{db} { 963 } 964 965 public: 966 967 // Return raw_traces, sorted ascending by the id. SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)968 static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db, 969 VersionedComponentName vcn) { 970 ScopedLockDb lock{db}; 971 972 const char* sql = 973 "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path " 974 "FROM raw_traces " 975 "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id " 976 "INNER JOIN activities ON activities.id = app_launch_histories.activity_id " 977 "INNER JOIN packages ON packages.id = activities.package_id " 978 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?" 979 "ORDER BY raw_traces.id ASC"; 980 981 DbStatement stmt = DbStatement::Prepare(db, 982 sql, 983 vcn.GetPackage(), 984 vcn.GetActivity(), 985 vcn.GetVersion()); 986 987 std::vector<RawTraceModel> results; 988 989 RawTraceModel p{db}; 990 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) { 991 results.push_back(p); 992 } 993 994 return results; 995 } 996 SelectByHistoryId(DbHandle db,int history_id)997 static std::optional<RawTraceModel> SelectByHistoryId(DbHandle db, int history_id) { 998 ScopedLockDb lock{db}; 999 1000 const char* sql = 1001 "SELECT id, history_id, file_path " 1002 "FROM raw_traces " 1003 "WHERE history_id = ?1 " 1004 "LIMIT 1;"; 1005 1006 DbStatement stmt = DbStatement::Prepare(db, sql, history_id); 1007 1008 RawTraceModel p{db}; 1009 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) { 1010 return std::nullopt; 1011 } 1012 1013 return p; 1014 } 1015 Insert(DbHandle db,int history_id,std::string file_path)1016 static std::optional<RawTraceModel> Insert(DbHandle db, 1017 int history_id, 1018 std::string file_path) { 1019 const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);"; 1020 1021 std::optional<int> inserted_row_id = 1022 DbQueryBuilder::Insert(db, sql, history_id, file_path); 1023 if (!inserted_row_id) { 1024 return std::nullopt; 1025 } 1026 1027 RawTraceModel p{db}; 1028 p.id = *inserted_row_id; 1029 p.history_id = history_id; 1030 p.file_path = file_path; 1031 1032 return p; 1033 } 1034 Delete()1035 bool Delete() { 1036 const char* sql = "DELETE FROM raw_traces WHERE id = ?"; 1037 1038 return DbQueryBuilder::Delete(db(), sql, id); 1039 } 1040 1041 int id; 1042 int history_id; 1043 std::string file_path; 1044 }; 1045 1046 inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) { 1047 os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ","; 1048 os << "file_path=" << p.file_path << "}"; 1049 return os; 1050 } 1051 1052 class PrefetchFileModel : public Model { 1053 protected: PrefetchFileModel(DbHandle db)1054 PrefetchFileModel(DbHandle db) : Model{db} { 1055 } 1056 1057 public: SelectByVersionedComponentName(DbHandle db,VersionedComponentName vcn)1058 static std::optional<PrefetchFileModel> SelectByVersionedComponentName( 1059 DbHandle db, 1060 VersionedComponentName vcn) { 1061 ScopedLockDb lock{db}; 1062 1063 const char* sql = 1064 "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path " 1065 "FROM prefetch_files " 1066 "INNER JOIN activities ON activities.id = prefetch_files.activity_id " 1067 "INNER JOIN packages ON packages.id = activities.package_id " 1068 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"; 1069 1070 DbStatement stmt = DbStatement::Prepare(db, 1071 sql, 1072 vcn.GetPackage(), 1073 vcn.GetActivity(), 1074 vcn.GetVersion()); 1075 1076 PrefetchFileModel p{db}; 1077 1078 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) { 1079 return std::nullopt; 1080 } 1081 1082 return p; 1083 } 1084 SelectAll(DbHandle db)1085 static std::vector<PrefetchFileModel> SelectAll(DbHandle db) { 1086 ScopedLockDb lock{db}; 1087 1088 std::string query = 1089 "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path " 1090 "FROM prefetch_files"; 1091 DbStatement stmt = DbStatement::Prepare(db, query); 1092 1093 std::vector<PrefetchFileModel> prefetch_files; 1094 PrefetchFileModel p{db}; 1095 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) { 1096 prefetch_files.push_back(p); 1097 } 1098 1099 return prefetch_files; 1100 } 1101 Insert(DbHandle db,int activity_id,std::string file_path)1102 static std::optional<PrefetchFileModel> Insert(DbHandle db, 1103 int activity_id, 1104 std::string file_path) { 1105 const char* sql = "INSERT INTO prefetch_files (activity_id, file_path) VALUES (?1, ?2);"; 1106 1107 std::optional<int> inserted_row_id = 1108 DbQueryBuilder::Insert(db, sql, activity_id, file_path); 1109 if (!inserted_row_id) { 1110 return std::nullopt; 1111 } 1112 1113 PrefetchFileModel p{db}; 1114 p.id = *inserted_row_id; 1115 p.activity_id = activity_id; 1116 p.file_path = file_path; 1117 1118 return p; 1119 } 1120 Delete()1121 bool Delete() { 1122 const char* sql = "DELETE FROM prefetch_files WHERE id = ?"; 1123 1124 return DbQueryBuilder::Delete(db(), sql, id); 1125 } 1126 1127 int id; 1128 int activity_id; 1129 std::string file_path; 1130 }; 1131 1132 inline std::ostream& operator<<(std::ostream& os, const PrefetchFileModel& p) { 1133 os << "PrefetchFileModel{id=" << p.id << ",activity_id=" << p.activity_id << ","; 1134 os << "file_path=" << p.file_path << "}"; 1135 return os; 1136 } 1137 1138 } // namespace iorap::db 1139 1140 #endif // IORAP_SRC_DB_MODELS_H_ 1141