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