blob: bfe8cfc34ed31d5c4cc847051fefe18068100f45 [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 Wang6e3196b2020-01-28 16:08:28 -080018#include "clean_up.h"
19#include "file_models.h"
Igor Murashkin03e5b052019-10-03 16:39:50 -070020
Yan Wang6e3196b2020-01-28 16:08:28 -080021#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 Wang6e3196b2020-01-28 16:08:28 -080037const 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 Wang6e3196b2020-01-28 16:08:28 -0800379 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 Wang6e3196b2020-01-28 16:08:28 -0800386 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 Wang6e3196b2020-01-28 16:08:28 -0800401 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 Wang6e3196b2020-01-28 16:08:28 -0800485 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 Wang6e3196b2020-01-28 16:08:28 -0800496 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 Wang6e3196b2020-01-28 16:08:28 -0800513 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 Wang6e3196b2020-01-28 16:08:28 -0800521 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 Wang6e3196b2020-01-28 16:08:28 -0800529 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 Wang6e3196b2020-01-28 16:08:28 -0800543
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
588 static std::optional<PackageModel> SelectByName(DbHandle db, const char* name) {
589 ScopedLockDb lock{db};
590
591 std::string query = "SELECT * FROM packages WHERE name = ?1 LIMIT 1;";
592 DbStatement stmt = DbStatement::Prepare(db, query, name);
593
594 PackageModel p{db};
595 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
596 return std::nullopt;
597 }
598
599 return p;
600 }
601
Yan Wang6e3196b2020-01-28 16:08:28 -0800602 static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db,
603 const char* name,
604 int version) {
605 ScopedLockDb lock{db};
606
607 std::string query =
608 "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;";
609 DbStatement stmt = DbStatement::Prepare(db, query, name, version);
610
611 PackageModel p{db};
612 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
613 return std::nullopt;
614 }
615
616 return p;
617 }
618
Yan Wangcdb97fa2019-10-18 18:18:48 -0700619 static std::vector<PackageModel> SelectAll(DbHandle db) {
620 ScopedLockDb lock{db};
621
622 std::string query = "SELECT * FROM packages;";
623 DbStatement stmt = DbStatement::Prepare(db, query);
624
625 std::vector<PackageModel> packages;
626 PackageModel p{db};
627 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
628 packages.push_back(p);
629 }
630
631 return packages;
632 }
633
Igor Murashkin03e5b052019-10-03 16:39:50 -0700634 static std::optional<PackageModel> Insert(DbHandle db,
635 std::string name,
Yan Wang6e3196b2020-01-28 16:08:28 -0800636 int version) {
Igor Murashkin03e5b052019-10-03 16:39:50 -0700637 const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
638
639 std::optional<int> inserted_row_id =
640 DbQueryBuilder::Insert(db, sql, name, version);
641 if (!inserted_row_id) {
642 return std::nullopt;
643 }
644
645 PackageModel p{db};
646 p.name = name;
647 p.version = version;
648 p.id = *inserted_row_id;
649
650 return p;
651 }
652
Yan Wang6e3196b2020-01-28 16:08:28 -0800653 bool Delete() {
654 const char* sql = "DELETE FROM packages WHERE id = ?";
655
656 return DbQueryBuilder::Delete(db(), sql, id);
657 }
658
Igor Murashkin03e5b052019-10-03 16:39:50 -0700659 int id;
660 std::string name;
Yan Wang6e3196b2020-01-28 16:08:28 -0800661 int version;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700662};
663
664inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
665 os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
666 os << "version=";
Yan Wang6e3196b2020-01-28 16:08:28 -0800667 os << p.version;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700668 os << "}";
669 return os;
670}
671
672class ActivityModel : public Model {
673 protected:
674 ActivityModel(DbHandle db) : Model{db} {
675 }
676
677 public:
678 static std::optional<ActivityModel> SelectById(DbHandle db, int id) {
679 ScopedLockDb lock{db};
680 int original_id = id;
681
682 std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;";
683 DbStatement stmt = DbStatement::Prepare(db, query, id);
684
685 ActivityModel p{db};
686 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
687 return std::nullopt;
688 }
689
690 return p;
691 }
692
693 static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db,
694 const char* name,
695 int package_id) {
696 ScopedLockDb lock{db};
697
698 std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;";
699 DbStatement stmt = DbStatement::Prepare(db, query, name, package_id);
700
701 ActivityModel p{db};
702 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
703 return std::nullopt;
704 }
705
706 return p;
707 }
708
Yan Wangcdb97fa2019-10-18 18:18:48 -0700709 static std::vector<ActivityModel> SelectByPackageId(DbHandle db,
710 int package_id) {
711 ScopedLockDb lock{db};
712
713 std::string query = "SELECT * FROM activities WHERE package_id = ?;";
714 DbStatement stmt = DbStatement::Prepare(db, query, package_id);
715
716 std::vector<ActivityModel> activities;
717 ActivityModel p{db};
718 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
719 activities.push_back(p);
720 }
721
722 return activities;
723 }
724
Igor Murashkin03e5b052019-10-03 16:39:50 -0700725 static std::optional<ActivityModel> Insert(DbHandle db,
726 std::string name,
727 int package_id) {
728 const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);";
729
730 std::optional<int> inserted_row_id =
731 DbQueryBuilder::Insert(db, sql, name, package_id);
732 if (!inserted_row_id) {
733 return std::nullopt;
734 }
735
736 ActivityModel p{db};
737 p.id = *inserted_row_id;
738 p.name = name;
739 p.package_id = package_id;
740
741 return p;
742 }
743
744 // Try to select by package_name+activity_name, otherwise insert into both tables.
745 // Package version is ignored for selects.
746 static std::optional<ActivityModel> SelectOrInsert(
747 DbHandle db,
748 std::string package_name,
Yan Wang6e3196b2020-01-28 16:08:28 -0800749 int package_version,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700750 std::string activity_name) {
Yan Wang6e3196b2020-01-28 16:08:28 -0800751 std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db,
752 package_name.c_str(),
753 package_version);
Igor Murashkin03e5b052019-10-03 16:39:50 -0700754 if (!package) {
755 package = PackageModel::Insert(db, package_name, package_version);
756 DCHECK(package.has_value());
757 }
758
759 std::optional<ActivityModel> activity =
760 ActivityModel::SelectByNameAndPackageId(db,
761 activity_name.c_str(),
762 package->id);
763 if (!activity) {
764 activity = Insert(db, activity_name, package->id);
765 // XX: should we really return an optional here? This feels like it should never fail.
766 }
767
768 return activity;
769 }
770
771 int id;
772 std::string name;
773 int package_id; // PackageModel::id
774};
775
776inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) {
777 os << "ActivityModel{id=" << p.id << ",name=" << p.name << ",";
778 os << "package_id=" << p.package_id << "}";
779 return os;
780}
781
782class AppLaunchHistoryModel : public Model {
783 protected:
784 AppLaunchHistoryModel(DbHandle db) : Model{db} {
785 }
786
787 public:
788 enum class Temperature : int32_t {
789 kUninitialized = -1, // Note: Not a valid SQL value.
790 kCold = 1,
791 kWarm = 2,
792 kHot = 3,
793 };
794
795 static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) {
796 ScopedLockDb lock{db};
797 int original_id = id;
798
799 std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;";
800 DbStatement stmt = DbStatement::Prepare(db, query, id);
801
802 AppLaunchHistoryModel p{db};
803 if (!DbQueryBuilder::SelectOnce(stmt,
804 p.id,
805 p.activity_id,
806 p.temperature,
807 p.trace_enabled,
808 p.readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700809 p.intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700810 p.total_time_ns,
811 p.report_fully_drawn_ns)) {
812 return std::nullopt;
813 }
814
815 return p;
816 }
817
Yan Wangcdb97fa2019-10-18 18:18:48 -0700818 // Selects the activity history for an activity id.
819 // The requirements are:
820 // * Should be cold run.
821 // * Pefetto trace is enabled.
822 // * intent_start_ns is *NOT* null.
823 static std::vector<AppLaunchHistoryModel> SelectActivityHistoryForCompile(
824 DbHandle db,
825 int activity_id) {
826 ScopedLockDb lock{db};
827 std::string query = "SELECT * FROM app_launch_histories "
828 "WHERE activity_id = ?1 AND"
829 " temperature = 1 AND"
Yan Wangd40dac32019-11-21 16:46:40 -0800830 " trace_enabled = TRUE AND"
831 " intent_started_ns IS NOT NULL;";
Yan Wangcdb97fa2019-10-18 18:18:48 -0700832 DbStatement stmt = DbStatement::Prepare(db, query, activity_id);
833 std::vector<AppLaunchHistoryModel> result;
834
835 AppLaunchHistoryModel p{db};
836 while (DbQueryBuilder::SelectOnce(stmt,
837 p.id,
838 p.activity_id,
839 p.temperature,
840 p.trace_enabled,
841 p.readahead_enabled,
Yan Wangd40dac32019-11-21 16:46:40 -0800842 p.intent_started_ns,
Yan Wangcdb97fa2019-10-18 18:18:48 -0700843 p.total_time_ns,
844 p.report_fully_drawn_ns)) {
845 result.push_back(p);
846 }
847 return result;
848 }
849
Igor Murashkin03e5b052019-10-03 16:39:50 -0700850 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db,
851 int activity_id,
852 AppLaunchHistoryModel::Temperature temperature,
853 bool trace_enabled,
854 bool readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700855 std::optional<uint64_t> intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700856 std::optional<uint64_t> total_time_ns,
857 std::optional<uint64_t> report_fully_drawn_ns)
858 {
859 const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, "
Yan Wang83dc3de2019-10-24 15:12:54 -0700860 "readahead_enabled, intent_started_ns, "
861 "total_time_ns, report_fully_drawn_ns) "
862 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700863
864 std::optional<int> inserted_row_id =
865 DbQueryBuilder::Insert(db,
866 sql,
867 activity_id,
868 temperature,
869 trace_enabled,
870 readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700871 intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700872 total_time_ns,
873 report_fully_drawn_ns);
874 if (!inserted_row_id) {
875 return std::nullopt;
876 }
877
878 AppLaunchHistoryModel p{db};
879 p.id = *inserted_row_id;
880 p.activity_id = activity_id;
881 p.temperature = temperature;
882 p.trace_enabled = trace_enabled;
883 p.readahead_enabled = readahead_enabled;
Yan Wang83dc3de2019-10-24 15:12:54 -0700884 p.intent_started_ns = intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700885 p.total_time_ns = total_time_ns;
886 p.report_fully_drawn_ns = report_fully_drawn_ns;
887
888 return p;
889 }
890
Yan Wang83dc3de2019-10-24 15:12:54 -0700891 static bool UpdateReportFullyDrawn(DbHandle db,
892 int history_id,
Yan Wang337e78b2019-11-22 14:31:39 -0800893 uint64_t report_fully_drawn_ns)
Yan Wang83dc3de2019-10-24 15:12:54 -0700894 {
895 const char* sql = "UPDATE app_launch_histories "
896 "SET report_fully_drawn_ns = ?1 "
897 "WHERE id = ?2;";
898
Yan Wang337e78b2019-11-22 14:31:39 -0800899 bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id);
Yan Wang83dc3de2019-10-24 15:12:54 -0700900
901 if (!result) {
902 LOG(ERROR)<< "Failed to update history_id:"<< history_id
Yan Wang337e78b2019-11-22 14:31:39 -0800903 << ", report_fully_drawn_ns: " << report_fully_drawn_ns;
Yan Wang83dc3de2019-10-24 15:12:54 -0700904 }
905 return result;
906 }
907
Igor Murashkin03e5b052019-10-03 16:39:50 -0700908 int id;
909 int activity_id; // ActivityModel::id
910 Temperature temperature = Temperature::kUninitialized;
911 bool trace_enabled;
912 bool readahead_enabled;
Yan Wang83dc3de2019-10-24 15:12:54 -0700913 std::optional<uint64_t> intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700914 std::optional<uint64_t> total_time_ns;
915 std::optional<uint64_t> report_fully_drawn_ns;
916};
917
918inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) {
919 os << "AppLaunchHistoryModel{id=" << p.id << ","
920 << "activity_id=" << p.activity_id << ","
921 << "temperature=" << static_cast<int>(p.temperature) << ","
922 << "trace_enabled=" << p.trace_enabled << ","
923 << "readahead_enabled=" << p.readahead_enabled << ","
Yan Wang83dc3de2019-10-24 15:12:54 -0700924 << "intent_started_ns=";
925 if (p.intent_started_ns) {
926 os << *p.intent_started_ns;
927 } else {
928 os << "(nullopt)";
929 }
930 os << ",";
931 os << "total_time_ns=";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700932 if (p.total_time_ns) {
933 os << *p.total_time_ns;
934 } else {
935 os << "(nullopt)";
936 }
937 os << ",";
938 os << "report_fully_drawn_ns=";
939 if (p.report_fully_drawn_ns) {
940 os << *p.report_fully_drawn_ns;
941 } else {
942 os << "(nullopt)";
943 }
944 os << "}";
945 return os;
946}
947
Igor Murashkin5e216982019-10-17 14:11:44 -0700948class RawTraceModel : public Model {
949 protected:
950 RawTraceModel(DbHandle db) : Model{db} {
951 }
952
953 public:
954
955 // Return raw_traces, sorted ascending by the id.
Yan Wang6e3196b2020-01-28 16:08:28 -0800956 static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db,
957 VersionedComponentName vcn) {
Igor Murashkin5e216982019-10-17 14:11:44 -0700958 ScopedLockDb lock{db};
959
960 const char* sql =
961 "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path "
962 "FROM raw_traces "
963 "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
964 "INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
965 "INNER JOIN packages ON packages.id = activities.package_id "
Yan Wang6e3196b2020-01-28 16:08:28 -0800966 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"
Igor Murashkin5e216982019-10-17 14:11:44 -0700967 "ORDER BY raw_traces.id ASC";
968
Yan Wang6e3196b2020-01-28 16:08:28 -0800969 DbStatement stmt = DbStatement::Prepare(db,
970 sql,
971 vcn.GetPackage(),
972 vcn.GetActivity(),
973 vcn.GetVersion());
Igor Murashkin5e216982019-10-17 14:11:44 -0700974
975 std::vector<RawTraceModel> results;
976
977 RawTraceModel p{db};
978 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
979 results.push_back(p);
980 }
981
982 return results;
983 }
984
Yan Wangcdb97fa2019-10-18 18:18:48 -0700985 static std::optional<RawTraceModel> SelectByHistoryId(DbHandle db, int history_id) {
986 ScopedLockDb lock{db};
987
988 const char* sql =
989 "SELECT id, history_id, file_path "
990 "FROM raw_traces "
991 "WHERE history_id = ?1 "
992 "LIMIT 1;";
993
994 DbStatement stmt = DbStatement::Prepare(db, sql, history_id);
995
996 RawTraceModel p{db};
997 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
998 return std::nullopt;
999 }
1000
1001 return p;
1002 }
1003
Igor Murashkin5e216982019-10-17 14:11:44 -07001004 static std::optional<RawTraceModel> Insert(DbHandle db,
1005 int history_id,
1006 std::string file_path) {
1007 const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);";
1008
1009 std::optional<int> inserted_row_id =
1010 DbQueryBuilder::Insert(db, sql, history_id, file_path);
1011 if (!inserted_row_id) {
1012 return std::nullopt;
1013 }
1014
1015 RawTraceModel p{db};
1016 p.id = *inserted_row_id;
1017 p.history_id = history_id;
1018 p.file_path = file_path;
1019
1020 return p;
1021 }
1022
1023 bool Delete() {
1024 const char* sql = "DELETE FROM raw_traces WHERE id = ?";
1025
1026 return DbQueryBuilder::Delete(db(), sql, id);
1027 }
1028
1029 int id;
1030 int history_id;
1031 std::string file_path;
1032};
1033
1034inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) {
1035 os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ",";
1036 os << "file_path=" << p.file_path << "}";
1037 return os;
1038}
1039
Yan Wangcdb97fa2019-10-18 18:18:48 -07001040class PrefetchFileModel : public Model {
1041 protected:
1042 PrefetchFileModel(DbHandle db) : Model{db} {
1043 }
1044
1045 public:
Yan Wang6e3196b2020-01-28 16:08:28 -08001046 static std::optional<PrefetchFileModel> SelectByVersionedComponentName(
1047 DbHandle db,
1048 VersionedComponentName vcn) {
Yan Wangd40dac32019-11-21 16:46:40 -08001049 ScopedLockDb lock{db};
1050
1051 const char* sql =
1052 "SELECT prefetch_files.id, prefetch_files.activity_id, prefetch_files.file_path "
1053 "FROM prefetch_files "
1054 "INNER JOIN activities ON activities.id = prefetch_files.activity_id "
1055 "INNER JOIN packages ON packages.id = activities.package_id "
Yan Wang6e3196b2020-01-28 16:08:28 -08001056 "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?";
Yan Wangd40dac32019-11-21 16:46:40 -08001057
Yan Wang6e3196b2020-01-28 16:08:28 -08001058 DbStatement stmt = DbStatement::Prepare(db,
1059 sql,
1060 vcn.GetPackage(),
1061 vcn.GetActivity(),
1062 vcn.GetVersion());
Yan Wangd40dac32019-11-21 16:46:40 -08001063
1064 PrefetchFileModel p{db};
1065
1066 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.activity_id, p.file_path)) {
1067 return std::nullopt;
1068 }
1069
1070 return p;
1071 }
1072
Yan Wangcdb97fa2019-10-18 18:18:48 -07001073 static std::optional<PrefetchFileModel> Insert(DbHandle db,
1074 int activity_id,
1075 std::string file_path) {
1076 const char* sql = "INSERT INTO prefetch_files (activity_id, file_path) VALUES (?1, ?2);";
1077
1078 std::optional<int> inserted_row_id =
1079 DbQueryBuilder::Insert(db, sql, activity_id, file_path);
1080 if (!inserted_row_id) {
1081 return std::nullopt;
1082 }
1083
1084 PrefetchFileModel p{db};
1085 p.id = *inserted_row_id;
1086 p.activity_id = activity_id;
1087 p.file_path = file_path;
1088
1089 return p;
1090 }
1091
1092 bool Delete() {
1093 const char* sql = "DELETE FROM prefetch_files WHERE id = ?";
1094
1095 return DbQueryBuilder::Delete(db(), sql, id);
1096 }
1097
1098 int id;
1099 int activity_id;
1100 std::string file_path;
1101};
1102
1103inline std::ostream& operator<<(std::ostream& os, const PrefetchFileModel& p) {
1104 os << "PrefetchFileModel{id=" << p.id << ",activity_id=" << p.activity_id << ",";
1105 os << "file_path=" << p.file_path << "}";
1106 return os;
1107}
1108
Igor Murashkin03e5b052019-10-03 16:39:50 -07001109} // namespace iorap::db
1110
1111#endif // IORAP_SRC_DB_MODELS_H_