blob: df6beb66ce3b5827b1891b6cdb5b855f751d8e2f [file] [log] [blame]
Igor Murashkin03e5b052019-10-03 16:39:50 -07001// 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
Yan Wang16b8fce2020-02-20 18:08:33 +000018#include "clean_up.h"
19#include "file_models.h"
Yan Wang6e3196b2020-01-28 16:08:28 -080020
Yan Wang16b8fce2020-02-20 18:08:33 +000021#include <android-base/logging.h>
22#include <utils/String8.h>
23
24#include <filesystem>
25#include <iostream>
Igor Murashkin03e5b052019-10-03 16:39:50 -070026#include <optional>
27#include <ostream>
28#include <string>
29#include <sstream>
30#include <type_traits>
Igor Murashkin5e216982019-10-17 14:11:44 -070031#include <vector>
Igor Murashkin03e5b052019-10-03 16:39:50 -070032
33#include <sqlite3.h>
34
35namespace iorap::db {
36
Yan Wang16b8fce2020-02-20 18:08:33 +000037const constexpr int kDbVersion = 2;
38
Igor Murashkin03e5b052019-10-03 16:39:50 -070039struct SqliteDbDeleter {
40 void operator()(sqlite3* db) {
41 if (db != nullptr) {
42 LOG(VERBOSE) << "sqlite3_close for: " << db;
43 sqlite3_close(db);
44 }
45 }
46};
47
48class DbHandle {
49 public:
50 // Take over ownership of sqlite3 db.
51 explicit DbHandle(sqlite3* db)
52 : db_{std::shared_ptr<sqlite3>{db, SqliteDbDeleter{}}},
53 mutex_{std::make_shared<std::mutex>()} {
54 }
55
56 sqlite3* get() {
57 return db_.get();
58 }
59
60 operator sqlite3*() {
61 return db_.get();
62 }
63
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
73class ScopedLockDb {
74 public:
75 ScopedLockDb(std::mutex& mutex) : mutex(mutex) {
76 mutex.lock();
77 }
78
79 ScopedLockDb(DbHandle& handle) : ScopedLockDb(handle.mutex()) {
80 }
81
82 ~ScopedLockDb() {
83 mutex.unlock();
84 }
85 private:
86 std::mutex& mutex;
87};
88
89class DbStatement {
90 public:
91 template <typename ... 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>
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
113 DbStatement(DbHandle db, sqlite3_stmt* stmt) : db_(db), stmt_(stmt) {
114 }
115
116 sqlite3_stmt* get() {
117 return stmt_;
118 }
119
120 DbHandle db() {
121 return db_;
122 }
123
Igor Murashkin5e216982019-10-17 14:11:44 -0700124 // Successive BindAll calls *do not* start back at the 0th bind position.
Igor Murashkin03e5b052019-10-03 16:39:50 -0700125 template <typename T, typename ... Args>
126 void BindAll(T&& arg, Args&&... args) {
127 Bind(std::forward<T>(arg));
128 BindAll(std::forward<Args>(args)...);
129 }
130
131 void BindAll() {}
132
133 template <typename T>
134 void Bind(const std::optional<T>& value) {
135 if (value) {
136 Bind(*value);
137 } else {
138 BindNull();
139 }
140 }
141
142 void Bind(bool value) {
143 CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value));
144 }
145
146 void Bind(int value) {
147 CheckOk(sqlite3_bind_int(stmt_, bind_counter_++, value));
148 }
149
150 void Bind(uint64_t value) {
151 CheckOk(sqlite3_bind_int64(stmt_, bind_counter_++, static_cast<int64_t>(value)));
152 }
153
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
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>>>
168 void Bind(E value) {
169 Bind(static_cast<std::underlying_type_t<E>>(value));
170 }
171
172 void BindNull() {
173 CheckOk(sqlite3_bind_null(stmt_, bind_counter_++));
174 }
175
176 int Step() {
177 ++step_counter_;
178 return sqlite3_step(stmt_);
179 }
180
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
Igor Murashkin5e216982019-10-17 14:11:44 -0700190 // Successive ColumnAll calls start at the 0th column again.
Igor Murashkin03e5b052019-10-03 16:39:50 -0700191 template <typename T, typename ... Args>
192 void ColumnAll(T& first, Args&... rest) {
193 Column(first);
194 ColumnAll(rest...);
Igor Murashkin5e216982019-10-17 14:11:44 -0700195 // Reset column counter back to 0
196 column_counter_ = 0;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700197 }
198
199 void ColumnAll() {}
200
201 template <typename T>
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>>>
217 void Column(E& value) {
218 std::underlying_type_t<E> tmp;
219 Column(/*out*/tmp);
220 value = static_cast<E>(tmp);
221 }
222
223 void Column(bool& value) {
224 value = sqlite3_column_int(stmt_, column_counter_++);
225 }
226
227 void Column(int& value) {
228 value = sqlite3_column_int(stmt_, column_counter_++);
229 }
230
231 void Column(uint64_t& value) {
232 value = static_cast<uint64_t>(sqlite3_column_int64(stmt_, column_counter_++));
233 }
234
235 void Column(std::string& value) {
236 const unsigned char* text = sqlite3_column_text(stmt_, column_counter_++);
Igor Murashkin5e216982019-10-17 14:11:44 -0700237
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
Igor Murashkin03e5b052019-10-03 16:39:50 -0700246 value = std::string{reinterpret_cast<const char*>(text)};
247 }
248
249 const char* ExpandedSql() {
250 char* p = sqlite3_expanded_sql(stmt_);
251 if (p == nullptr) {
252 return "(nullptr)";
253 }
254 return p;
255 }
256
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
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
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
297class DbQueryBuilder {
298 public:
299 // Returns the row ID that was inserted last.
300 template <typename... 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
Igor Murashkin5e216982019-10-17 14:11:44 -0700317 template <typename... Args>
318 static bool Delete(DbHandle db, const std::string& sql, Args&&... args) {
Yan Wang83dc3de2019-10-24 15:12:54 -0700319 return ExecuteOnce(db, sql, std::forward<Args>(args)...);
320 }
321
322 template <typename... 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>
328 static bool ExecuteOnce(DbHandle db, const std::string& sql, Args&&... args) {
Igor Murashkin5e216982019-10-17 14:11:44 -0700329 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
Igor Murashkin03e5b052019-10-03 16:39:50 -0700340 template <typename... 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
356class Model {
357 public:
358 DbHandle db() const {
359 return db_;
360 }
361
362 Model(DbHandle db) : db_{db} {
363 }
364
365 private:
366 DbHandle db_;
367};
368
369class SchemaModel : public Model {
370 public:
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;
Yan Wang16b8fce2020-02-20 18:08:33 +0000379 bool is_deprecated = false;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700380 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 << "'";
Yan Wang16b8fce2020-02-20 18:08:33 +0000386 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 }
Igor Murashkin03e5b052019-10-03 16:39:50 -0700398 }
399 }
400
Yan Wang16b8fce2020-02-20 18:08:33 +0000401 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
Igor Murashkin03e5b052019-10-03 16:39:50 -0700407 // 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
424 void MarkSingleton() {
425 s_singleton_ = db();
426 }
427
428 static DbHandle GetSingleton() {
429 DCHECK(s_singleton_.has_value());
430 return *s_singleton_;
431 }
432
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
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:
470 SchemaModel(DbHandle db, std::string location) : Model{db}, location_(location) {
471 }
472
473 private:
474 static std::optional<DbHandle> s_singleton_;
475
476 void CreateSchema() {
477 const char* sql_to_initialize = R"SQLC0D3(
478 CREATE TABLE schema_versions(
479 version INTEGER NOT NULL
480 );
Igor Murashkin03e5b052019-10-03 16:39:50 -0700481
482 CREATE TABLE packages(
483 id INTEGER NOT NULL,
484 name TEXT NOT NULL,
Yan Wang16b8fce2020-02-20 18:08:33 +0000485 version INTEGER NOT NULL,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700486
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),
Yan Wang16b8fce2020-02-20 18:08:33 +0000496 FOREIGN KEY (package_id) REFERENCES packages (id) ON DELETE CASCADE
Igor Murashkin03e5b052019-10-03 16:39:50 -0700497 );
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
Yan Wang16b8fce2020-02-20 18:08:33 +0000513 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
Igor Murashkin03e5b052019-10-03 16:39:50 -0700514 );
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
Yan Wang16b8fce2020-02-20 18:08:33 +0000521 FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE
Igor Murashkin03e5b052019-10-03 16:39:50 -0700522 );
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
Yan Wang16b8fce2020-02-20 18:08:33 +0000529 FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
Igor Murashkin03e5b052019-10-03 16:39:50 -0700530 );
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 }
Yan Wang16b8fce2020-02-20 18:08:33 +0000543
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 }
Igor Murashkin03e5b052019-10-03 16:39:50 -0700558 }
559
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
567class PackageModel : public Model {
568 protected:
569 PackageModel(DbHandle db) : Model{db} {
570 }
571
572 public:
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
Igor Murashkina35242b2020-03-11 13:28:51 -0700588 static std::vector<PackageModel> SelectByName(DbHandle db, const char* name) {
Igor Murashkin03e5b052019-10-03 16:39:50 -0700589 ScopedLockDb lock{db};
590
Igor Murashkina35242b2020-03-11 13:28:51 -0700591 std::string query = "SELECT * FROM packages WHERE name = ?1;";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700592 DbStatement stmt = DbStatement::Prepare(db, query, name);
593
Igor Murashkina35242b2020-03-11 13:28:51 -0700594 std::vector<PackageModel> packages;
595
Igor Murashkin03e5b052019-10-03 16:39:50 -0700596 PackageModel p{db};
Igor Murashkina35242b2020-03-11 13:28:51 -0700597 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
598 packages.push_back(p);
Igor Murashkin03e5b052019-10-03 16:39:50 -0700599 }
600
Igor Murashkina35242b2020-03-11 13:28:51 -0700601 return packages;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700602 }
603
Yan Wang16b8fce2020-02-20 18:08:33 +0000604 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
Yan Wangcdb97fa2019-10-18 18:18:48 -0700621 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
Igor Murashkin03e5b052019-10-03 16:39:50 -0700636 static std::optional<PackageModel> Insert(DbHandle db,
637 std::string name,
Yan Wang16b8fce2020-02-20 18:08:33 +0000638 int version) {
Igor Murashkin03e5b052019-10-03 16:39:50 -0700639 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
Yan Wang16b8fce2020-02-20 18:08:33 +0000655 bool Delete() {
656 const char* sql = "DELETE FROM packages WHERE id = ?";
657
658 return DbQueryBuilder::Delete(db(), sql, id);
659 }
660
Igor Murashkin03e5b052019-10-03 16:39:50 -0700661 int id;
662 std::string name;
Yan Wang16b8fce2020-02-20 18:08:33 +0000663 int version;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700664};
665
666inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
667 os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
668 os << "version=";
Yan Wang16b8fce2020-02-20 18:08:33 +0000669 os << p.version;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700670 os << "}";
671 return os;
672}
673
674class ActivityModel : public Model {
675 protected:
676 ActivityModel(DbHandle db) : Model{db} {
677 }
678
679 public:
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
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
Yan Wangcdb97fa2019-10-18 18:18:48 -0700711 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
Igor Murashkin03e5b052019-10-03 16:39:50 -0700727 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.
748 static std::optional<ActivityModel> SelectOrInsert(
749 DbHandle db,
750 std::string package_name,
Yan Wang16b8fce2020-02-20 18:08:33 +0000751 int package_version,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700752 std::string activity_name) {
Yan Wang16b8fce2020-02-20 18:08:33 +0000753 std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db,
754 package_name.c_str(),
755 package_version);
Igor Murashkin03e5b052019-10-03 16:39:50 -0700756 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
778inline 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
784class AppLaunchHistoryModel : public Model {
785 protected:
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
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,
Yan Wang83dc3de2019-10-24 15:12:54 -0700811 p.intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700812 p.total_time_ns,
813 p.report_fully_drawn_ns)) {
814 return std::nullopt;
815 }
816
817 return p;
818 }
819
Yan Wangcdb97fa2019-10-18 18:18:48 -0700820 // 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.
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"
Yan Wangd40dac32019-11-21 16:46:40 -0800832 " trace_enabled = TRUE AND"
833 " intent_started_ns IS NOT NULL;";
Yan Wangcdb97fa2019-10-18 18:18:48 -0700834 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,
Yan Wangd40dac32019-11-21 16:46:40 -0800844 p.intent_started_ns,
Yan Wangcdb97fa2019-10-18 18:18:48 -0700845 p.total_time_ns,
846 p.report_fully_drawn_ns)) {
847 result.push_back(p);
848 }
849 return result;
850 }
851
Igor Murashkin03e5b052019-10-03 16:39:50 -0700852 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db,
853 int activity_id,
854 AppLaunchHistoryModel::Temperature temperature,
855 bool trace_enabled,
856 bool readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700857 std::optional<uint64_t> intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700858 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, "
Yan Wang83dc3de2019-10-24 15:12:54 -0700862 "readahead_enabled, intent_started_ns, "
863 "total_time_ns, report_fully_drawn_ns) "
864 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700865
866 std::optional<int> inserted_row_id =
867 DbQueryBuilder::Insert(db,
868 sql,
869 activity_id,
870 temperature,
871 trace_enabled,
872 readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700873 intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700874 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;
Yan Wang83dc3de2019-10-24 15:12:54 -0700886 p.intent_started_ns = intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700887 p.total_time_ns = total_time_ns;
888 p.report_fully_drawn_ns = report_fully_drawn_ns;
889
890 return p;
891 }
892
Yan Wang83dc3de2019-10-24 15:12:54 -0700893 static bool UpdateReportFullyDrawn(DbHandle db,
894 int history_id,
Yan Wang337e78b2019-11-22 14:31:39 -0800895 uint64_t report_fully_drawn_ns)
Yan Wang83dc3de2019-10-24 15:12:54 -0700896 {
897 const char* sql = "UPDATE app_launch_histories "
898 "SET report_fully_drawn_ns = ?1 "
899 "WHERE id = ?2;";
900
Yan Wang337e78b2019-11-22 14:31:39 -0800901 bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id);
Yan Wang83dc3de2019-10-24 15:12:54 -0700902
903 if (!result) {
904 LOG(ERROR)<< "Failed to update history_id:"<< history_id
Yan Wang337e78b2019-11-22 14:31:39 -0800905 << ", report_fully_drawn_ns: " << report_fully_drawn_ns;
Yan Wang83dc3de2019-10-24 15:12:54 -0700906 }
907 return result;
908 }
909
Igor Murashkin03e5b052019-10-03 16:39:50 -0700910 int id;
911 int activity_id; // ActivityModel::id
912 Temperature temperature = Temperature::kUninitialized;
913 bool trace_enabled;
914 bool readahead_enabled;
Yan Wang83dc3de2019-10-24 15:12:54 -0700915 std::optional<uint64_t> intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700916 std::optional<uint64_t> total_time_ns;
917 std::optional<uint64_t> report_fully_drawn_ns;
918};
919
920inline 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 << ","
Yan Wang83dc3de2019-10-24 15:12:54 -0700926 << "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=";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700934 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
Igor Murashkin5e216982019-10-17 14:11:44 -0700950class RawTraceModel : public Model {
951 protected:
952 RawTraceModel(DbHandle db) : Model{db} {
953 }
954
955 public:
956
957 // Return raw_traces, sorted ascending by the id.
Yan Wang16b8fce2020-02-20 18:08:33 +0000958 static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db,
959 VersionedComponentName vcn) {
Igor Murashkin5e216982019-10-17 14:11:44 -0700960 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 "
Yan Wang16b8fce2020-02-20 18:08:33 +0000968 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"
Igor Murashkin5e216982019-10-17 14:11:44 -0700969 "ORDER BY raw_traces.id ASC";
970
Yan Wang16b8fce2020-02-20 18:08:33 +0000971 DbStatement stmt = DbStatement::Prepare(db,
972 sql,
973 vcn.GetPackage(),
974 vcn.GetActivity(),
975 vcn.GetVersion());
Igor Murashkin5e216982019-10-17 14:11:44 -0700976
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
Yan Wangcdb97fa2019-10-18 18:18:48 -0700987 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
Igor Murashkin5e216982019-10-17 14:11:44 -07001006 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
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
1036inline 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
Yan Wangcdb97fa2019-10-18 18:18:48 -07001042class PrefetchFileModel : public Model {
1043 protected:
1044 PrefetchFileModel(DbHandle db) : Model{db} {
1045 }
1046
1047 public:
Yan Wang16b8fce2020-02-20 18:08:33 +00001048 static std::optional<PrefetchFileModel> SelectByVersionedComponentName(
1049 DbHandle db,
1050 VersionedComponentName vcn) {
Yan Wangd40dac32019-11-21 16:46:40 -08001051 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 "
Yan Wang16b8fce2020-02-20 18:08:33 +00001058 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?";
Yan Wangd40dac32019-11-21 16:46:40 -08001059
Yan Wang16b8fce2020-02-20 18:08:33 +00001060 DbStatement stmt = DbStatement::Prepare(db,
1061 sql,
1062 vcn.GetPackage(),
1063 vcn.GetActivity(),
1064 vcn.GetVersion());
Yan Wangd40dac32019-11-21 16:46:40 -08001065
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
Igor Murashkin0a7cb8c2020-03-03 15:57:05 -08001075 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
Yan Wangcdb97fa2019-10-18 18:18:48 -07001092 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
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
1122inline 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
Igor Murashkin03e5b052019-10-03 16:39:50 -07001128} // namespace iorap::db
1129
1130#endif // IORAP_SRC_DB_MODELS_H_