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