blob: 94f82dabe3bb21110fc5e41c32d7884c6fae7e4f [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 template <typename... Args>
310 static bool Delete(DbHandle db, const std::string& sql, Args&&... args) {
Yan Wang83dc3de2019-10-24 15:12:54 -0700311 return ExecuteOnce(db, sql, std::forward<Args>(args)...);
312 }
313
314 template <typename... Args>
315 static bool Update(DbHandle db, const std::string& sql, Args&&... args) {
316 return ExecuteOnce(db, sql, std::forward<Args>(args)...);
317 }
318
319 template <typename... Args>
320 static bool ExecuteOnce(DbHandle db, const std::string& sql, Args&&... args) {
Igor Murashkin5e216982019-10-17 14:11:44 -0700321 ScopedLockDb lock{db};
322
323 DbStatement stmt = DbStatement::Prepare(db, sql, std::forward<Args>(args)...);
324
325 if (!stmt.Step(SQLITE_DONE)) {
326 return false;
327 }
328
329 return true;
330 }
331
Igor Murashkin03e5b052019-10-03 16:39:50 -0700332 template <typename... Args>
333 static bool SelectOnce(DbStatement& stmt, Args&... args) {
334 int rc = stmt.Step();
335 switch (rc) {
336 case SQLITE_ROW:
337 stmt.ColumnAll(/*out*/args...);
338 return true;
339 case SQLITE_DONE:
340 return false;
341 default:
342 LOG(ERROR) << "Failed to step (" << rc << "): " << sqlite3_errmsg(stmt.db());
343 return false;
344 }
345 }
346};
347
348class Model {
349 public:
350 DbHandle db() const {
351 return db_;
352 }
353
354 Model(DbHandle db) : db_{db} {
355 }
356
357 private:
358 DbHandle db_;
359};
360
361class SchemaModel : public Model {
362 public:
363 static SchemaModel GetOrCreate(std::string location) {
364 int rc = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, /*data*/nullptr);
365
366 if (rc != SQLITE_OK) {
367 LOG(FATAL) << "Failed to configure logging";
368 }
369
370 sqlite3* db = nullptr;
371 if (location != ":memory:") {
372 // Try to open DB if it already exists.
373 rc = sqlite3_open_v2(location.c_str(), /*out*/&db, SQLITE_OPEN_READWRITE, /*vfs*/nullptr);
374
375 if (rc == SQLITE_OK) {
376 LOG(INFO) << "Opened existing database at '" << location << "'";
377 return SchemaModel{DbHandle{db}, location};
378 }
379 }
380
381 // Create a new DB if one didn't exist already.
382 rc = sqlite3_open(location.c_str(), /*out*/&db);
383
384 if (rc != SQLITE_OK) {
385 LOG(FATAL) << "Failed to open DB: " << sqlite3_errmsg(db);
386 }
387
388 SchemaModel schema{DbHandle{db}, location};
389 schema.Reinitialize();
390 // TODO: migrate versions upwards when we rev the schema version
391
392 int old_version = schema.Version();
393 LOG(VERBOSE) << "Loaded schema version: " << old_version;
394
395 return schema;
396 }
397
398 void MarkSingleton() {
399 s_singleton_ = db();
400 }
401
402 static DbHandle GetSingleton() {
403 DCHECK(s_singleton_.has_value());
404 return *s_singleton_;
405 }
406
407 void Reinitialize() {
408 const char* sql_to_initialize = R"SQLC0D3(
409 DROP TABLE IF EXISTS schema_versions;
410 DROP TABLE IF EXISTS packages;
411 DROP TABLE IF EXISTS activities;
412 DROP TABLE IF EXISTS app_launch_histories;
413 DROP TABLE IF EXISTS raw_traces;
414 DROP TABLE IF EXISTS prefetch_files;
415)SQLC0D3";
416 char* err_msg = nullptr;
417 int rc = sqlite3_exec(db().get(),
418 sql_to_initialize,
419 /*callback*/nullptr,
420 /*arg*/0,
421 /*out*/&err_msg);
422 if (rc != SQLITE_OK) {
423 LOG(FATAL) << "Failed to drop tables: " << err_msg ? err_msg : "nullptr";
424 }
425
426 CreateSchema();
427 LOG(INFO) << "Reinitialized database at '" << location_ << "'";
428 }
429
430 int Version() {
431 std::string query = "SELECT MAX(version) FROM schema_versions;";
432 DbStatement stmt = DbStatement::Prepare(db(), query);
433
434 int return_value = 0;
435 if (!DbQueryBuilder::SelectOnce(stmt, /*out*/return_value)) {
436 LOG(ERROR) << "Failed to query schema version";
437 return -1;
438 }
439
440 return return_value;
441 }
442
443 protected:
444 SchemaModel(DbHandle db, std::string location) : Model{db}, location_(location) {
445 }
446
447 private:
448 static std::optional<DbHandle> s_singleton_;
449
450 void CreateSchema() {
451 const char* sql_to_initialize = R"SQLC0D3(
452 CREATE TABLE schema_versions(
453 version INTEGER NOT NULL
454 );
455 INSERT INTO schema_versions VALUES(1);
456
457 CREATE TABLE packages(
458 id INTEGER NOT NULL,
459 name TEXT NOT NULL,
460 version INTEGER,
461
462 PRIMARY KEY(id)
463 );
464
465 CREATE TABLE activities(
466 id INTEGER NOT NULL,
467 name TEXT NOT NULL,
468 package_id INTEGER NOT NULL,
469
470 PRIMARY KEY(id),
471 FOREIGN KEY (package_id) REFERENCES packages (id)
472 );
473
474 CREATE TABLE app_launch_histories(
475 id INTEGER NOT NULL PRIMARY KEY,
476 activity_id INTEGER NOT NULL,
477 -- 1:Cold, 2:Warm, 3:Hot
478 temperature INTEGER CHECK (temperature IN (1, 2, 3)) NOT NULL,
479 trace_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL,
480 readahead_enabled INTEGER CHECK(trace_enabled in (TRUE, FALSE)) NOT NULL,
481 -- absolute timestamp since epoch
482 intent_started_ns INTEGER CHECK(intent_started_ns IS NULL or intent_started_ns >= 0),
483 -- absolute timestamp since epoch
484 total_time_ns INTEGER CHECK(total_time_ns IS NULL or total_time_ns >= 0),
485 -- absolute timestamp since epoch
486 report_fully_drawn_ns INTEGER CHECK(report_fully_drawn_ns IS NULL or report_fully_drawn_ns >= 0),
487
488 FOREIGN KEY (activity_id) REFERENCES activities (id)
489 );
490
491 CREATE TABLE raw_traces(
492 id INTEGER NOT NULL PRIMARY KEY,
493 history_id INTEGER NOT NULL,
494 file_path TEXT NOT NULL,
495
496 FOREIGN KEY (history_id) REFERENCES app_launch_histories (id)
497 );
498
499 CREATE TABLE prefetch_files(
500 id INTEGER NOT NULL PRIMARY KEY,
501 activity_id INTEGER NOT NULL,
502 file_path TEXT NOT NULL,
503
504 FOREIGN KEY (activity_id) REFERENCES activities (id)
505 );
506)SQLC0D3";
507
508 char* err_msg = nullptr;
509 int rc = sqlite3_exec(db().get(),
510 sql_to_initialize,
511 /*callback*/nullptr,
512 /*arg*/0,
513 /*out*/&err_msg);
514
515 if (rc != SQLITE_OK) {
516 LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr";
517 }
518 }
519
520 static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) {
521 LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg;
522 }
523
524 std::string location_;
525};
526
527class PackageModel : public Model {
528 protected:
529 PackageModel(DbHandle db) : Model{db} {
530 }
531
532 public:
533 static std::optional<PackageModel> SelectById(DbHandle db, int id) {
534 ScopedLockDb lock{db};
535 int original_id = id;
536
537 std::string query = "SELECT * FROM packages WHERE id = ?1 LIMIT 1;";
538 DbStatement stmt = DbStatement::Prepare(db, query, id);
539
540 PackageModel p{db};
541 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
542 return std::nullopt;
543 }
544
545 return p;
546 }
547
548 static std::optional<PackageModel> SelectByName(DbHandle db, const char* name) {
549 ScopedLockDb lock{db};
550
551 std::string query = "SELECT * FROM packages WHERE name = ?1 LIMIT 1;";
552 DbStatement stmt = DbStatement::Prepare(db, query, name);
553
554 PackageModel p{db};
555 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
556 return std::nullopt;
557 }
558
559 return p;
560 }
561
Yan Wangcdb97fa2019-10-18 18:18:48 -0700562 static std::vector<PackageModel> SelectAll(DbHandle db) {
563 ScopedLockDb lock{db};
564
565 std::string query = "SELECT * FROM packages;";
566 DbStatement stmt = DbStatement::Prepare(db, query);
567
568 std::vector<PackageModel> packages;
569 PackageModel p{db};
570 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
571 packages.push_back(p);
572 }
573
574 return packages;
575 }
576
Igor Murashkin03e5b052019-10-03 16:39:50 -0700577 static std::optional<PackageModel> Insert(DbHandle db,
578 std::string name,
579 std::optional<int> version) {
580 const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
581
582 std::optional<int> inserted_row_id =
583 DbQueryBuilder::Insert(db, sql, name, version);
584 if (!inserted_row_id) {
585 return std::nullopt;
586 }
587
588 PackageModel p{db};
589 p.name = name;
590 p.version = version;
591 p.id = *inserted_row_id;
592
593 return p;
594 }
595
596 int id;
597 std::string name;
598 std::optional<int> version;
599};
600
601inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
602 os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
603 os << "version=";
604 if (p.version) {
605 os << *p.version;
606 } else {
607 os << "(nullopt)";
608 }
609 os << "}";
610 return os;
611}
612
613class ActivityModel : public Model {
614 protected:
615 ActivityModel(DbHandle db) : Model{db} {
616 }
617
618 public:
619 static std::optional<ActivityModel> SelectById(DbHandle db, int id) {
620 ScopedLockDb lock{db};
621 int original_id = id;
622
623 std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;";
624 DbStatement stmt = DbStatement::Prepare(db, query, id);
625
626 ActivityModel p{db};
627 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
628 return std::nullopt;
629 }
630
631 return p;
632 }
633
634 static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db,
635 const char* name,
636 int package_id) {
637 ScopedLockDb lock{db};
638
639 std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;";
640 DbStatement stmt = DbStatement::Prepare(db, query, name, package_id);
641
642 ActivityModel p{db};
643 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
644 return std::nullopt;
645 }
646
647 return p;
648 }
649
Yan Wangcdb97fa2019-10-18 18:18:48 -0700650 static std::vector<ActivityModel> SelectByPackageId(DbHandle db,
651 int package_id) {
652 ScopedLockDb lock{db};
653
654 std::string query = "SELECT * FROM activities WHERE package_id = ?;";
655 DbStatement stmt = DbStatement::Prepare(db, query, package_id);
656
657 std::vector<ActivityModel> activities;
658 ActivityModel p{db};
659 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
660 activities.push_back(p);
661 }
662
663 return activities;
664 }
665
Igor Murashkin03e5b052019-10-03 16:39:50 -0700666 static std::optional<ActivityModel> Insert(DbHandle db,
667 std::string name,
668 int package_id) {
669 const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);";
670
671 std::optional<int> inserted_row_id =
672 DbQueryBuilder::Insert(db, sql, name, package_id);
673 if (!inserted_row_id) {
674 return std::nullopt;
675 }
676
677 ActivityModel p{db};
678 p.id = *inserted_row_id;
679 p.name = name;
680 p.package_id = package_id;
681
682 return p;
683 }
684
685 // Try to select by package_name+activity_name, otherwise insert into both tables.
686 // Package version is ignored for selects.
687 static std::optional<ActivityModel> SelectOrInsert(
688 DbHandle db,
689 std::string package_name,
690 std::optional<int> package_version,
691 std::string activity_name) {
692 std::optional<PackageModel> package = PackageModel::SelectByName(db, package_name.c_str());
693 if (!package) {
694 package = PackageModel::Insert(db, package_name, package_version);
695 DCHECK(package.has_value());
696 }
697
698 std::optional<ActivityModel> activity =
699 ActivityModel::SelectByNameAndPackageId(db,
700 activity_name.c_str(),
701 package->id);
702 if (!activity) {
703 activity = Insert(db, activity_name, package->id);
704 // XX: should we really return an optional here? This feels like it should never fail.
705 }
706
707 return activity;
708 }
709
710 int id;
711 std::string name;
712 int package_id; // PackageModel::id
713};
714
715inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) {
716 os << "ActivityModel{id=" << p.id << ",name=" << p.name << ",";
717 os << "package_id=" << p.package_id << "}";
718 return os;
719}
720
721class AppLaunchHistoryModel : public Model {
722 protected:
723 AppLaunchHistoryModel(DbHandle db) : Model{db} {
724 }
725
726 public:
727 enum class Temperature : int32_t {
728 kUninitialized = -1, // Note: Not a valid SQL value.
729 kCold = 1,
730 kWarm = 2,
731 kHot = 3,
732 };
733
734 static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) {
735 ScopedLockDb lock{db};
736 int original_id = id;
737
738 std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;";
739 DbStatement stmt = DbStatement::Prepare(db, query, id);
740
741 AppLaunchHistoryModel p{db};
742 if (!DbQueryBuilder::SelectOnce(stmt,
743 p.id,
744 p.activity_id,
745 p.temperature,
746 p.trace_enabled,
747 p.readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700748 p.intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700749 p.total_time_ns,
750 p.report_fully_drawn_ns)) {
751 return std::nullopt;
752 }
753
754 return p;
755 }
756
Yan Wangcdb97fa2019-10-18 18:18:48 -0700757 // Selects the activity history for an activity id.
758 // The requirements are:
759 // * Should be cold run.
760 // * Pefetto trace is enabled.
761 // * intent_start_ns is *NOT* null.
762 static std::vector<AppLaunchHistoryModel> SelectActivityHistoryForCompile(
763 DbHandle db,
764 int activity_id) {
765 ScopedLockDb lock{db};
766 std::string query = "SELECT * FROM app_launch_histories "
767 "WHERE activity_id = ?1 AND"
768 " temperature = 1 AND"
769 " trace_enabled = TRUE"
770 " intent_started_ns != NULL;";
771 DbStatement stmt = DbStatement::Prepare(db, query, activity_id);
772 std::vector<AppLaunchHistoryModel> result;
773
774 AppLaunchHistoryModel p{db};
775 while (DbQueryBuilder::SelectOnce(stmt,
776 p.id,
777 p.activity_id,
778 p.temperature,
779 p.trace_enabled,
780 p.readahead_enabled,
781 p.total_time_ns,
782 p.report_fully_drawn_ns)) {
783 result.push_back(p);
784 }
785 return result;
786 }
787
Igor Murashkin03e5b052019-10-03 16:39:50 -0700788 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db,
789 int activity_id,
790 AppLaunchHistoryModel::Temperature temperature,
791 bool trace_enabled,
792 bool readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700793 std::optional<uint64_t> intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700794 std::optional<uint64_t> total_time_ns,
795 std::optional<uint64_t> report_fully_drawn_ns)
796 {
797 const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, "
Yan Wang83dc3de2019-10-24 15:12:54 -0700798 "readahead_enabled, intent_started_ns, "
799 "total_time_ns, report_fully_drawn_ns) "
800 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700801
802 std::optional<int> inserted_row_id =
803 DbQueryBuilder::Insert(db,
804 sql,
805 activity_id,
806 temperature,
807 trace_enabled,
808 readahead_enabled,
Yan Wang83dc3de2019-10-24 15:12:54 -0700809 intent_started_ns,
Igor Murashkin03e5b052019-10-03 16:39:50 -0700810 total_time_ns,
811 report_fully_drawn_ns);
812 if (!inserted_row_id) {
813 return std::nullopt;
814 }
815
816 AppLaunchHistoryModel p{db};
817 p.id = *inserted_row_id;
818 p.activity_id = activity_id;
819 p.temperature = temperature;
820 p.trace_enabled = trace_enabled;
821 p.readahead_enabled = readahead_enabled;
Yan Wang83dc3de2019-10-24 15:12:54 -0700822 p.intent_started_ns = intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700823 p.total_time_ns = total_time_ns;
824 p.report_fully_drawn_ns = report_fully_drawn_ns;
825
826 return p;
827 }
828
Yan Wang83dc3de2019-10-24 15:12:54 -0700829 static bool UpdateReportFullyDrawn(DbHandle db,
830 int history_id,
Yan Wang337e78b2019-11-22 14:31:39 -0800831 uint64_t report_fully_drawn_ns)
Yan Wang83dc3de2019-10-24 15:12:54 -0700832 {
833 const char* sql = "UPDATE app_launch_histories "
834 "SET report_fully_drawn_ns = ?1 "
835 "WHERE id = ?2;";
836
Yan Wang337e78b2019-11-22 14:31:39 -0800837 bool result = DbQueryBuilder::Update(db, sql, report_fully_drawn_ns, history_id);
Yan Wang83dc3de2019-10-24 15:12:54 -0700838
839 if (!result) {
840 LOG(ERROR)<< "Failed to update history_id:"<< history_id
Yan Wang337e78b2019-11-22 14:31:39 -0800841 << ", report_fully_drawn_ns: " << report_fully_drawn_ns;
Yan Wang83dc3de2019-10-24 15:12:54 -0700842 }
843 return result;
844 }
845
Igor Murashkin03e5b052019-10-03 16:39:50 -0700846 int id;
847 int activity_id; // ActivityModel::id
848 Temperature temperature = Temperature::kUninitialized;
849 bool trace_enabled;
850 bool readahead_enabled;
Yan Wang83dc3de2019-10-24 15:12:54 -0700851 std::optional<uint64_t> intent_started_ns;
Igor Murashkin03e5b052019-10-03 16:39:50 -0700852 std::optional<uint64_t> total_time_ns;
853 std::optional<uint64_t> report_fully_drawn_ns;
854};
855
856inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) {
857 os << "AppLaunchHistoryModel{id=" << p.id << ","
858 << "activity_id=" << p.activity_id << ","
859 << "temperature=" << static_cast<int>(p.temperature) << ","
860 << "trace_enabled=" << p.trace_enabled << ","
861 << "readahead_enabled=" << p.readahead_enabled << ","
Yan Wang83dc3de2019-10-24 15:12:54 -0700862 << "intent_started_ns=";
863 if (p.intent_started_ns) {
864 os << *p.intent_started_ns;
865 } else {
866 os << "(nullopt)";
867 }
868 os << ",";
869 os << "total_time_ns=";
Igor Murashkin03e5b052019-10-03 16:39:50 -0700870 if (p.total_time_ns) {
871 os << *p.total_time_ns;
872 } else {
873 os << "(nullopt)";
874 }
875 os << ",";
876 os << "report_fully_drawn_ns=";
877 if (p.report_fully_drawn_ns) {
878 os << *p.report_fully_drawn_ns;
879 } else {
880 os << "(nullopt)";
881 }
882 os << "}";
883 return os;
884}
885
Igor Murashkin5e216982019-10-17 14:11:44 -0700886class RawTraceModel : public Model {
887 protected:
888 RawTraceModel(DbHandle db) : Model{db} {
889 }
890
891 public:
892
893 // Return raw_traces, sorted ascending by the id.
894 static std::vector<RawTraceModel> SelectByPackageNameActivityName(DbHandle db,
895 std::string package_name,
896 std::string activity_name) {
897 ScopedLockDb lock{db};
898
899 const char* sql =
900 "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path "
901 "FROM raw_traces "
902 "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
903 "INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
904 "INNER JOIN packages ON packages.id = activities.package_id "
905 "WHERE packages.name = ? AND activities.name = ? "
906 "ORDER BY raw_traces.id ASC";
907
908 DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
909
910 std::vector<RawTraceModel> results;
911
912 RawTraceModel p{db};
913 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
914 results.push_back(p);
915 }
916
917 return results;
918 }
919
Yan Wangcdb97fa2019-10-18 18:18:48 -0700920 static std::optional<RawTraceModel> SelectByHistoryId(DbHandle db, int history_id) {
921 ScopedLockDb lock{db};
922
923 const char* sql =
924 "SELECT id, history_id, file_path "
925 "FROM raw_traces "
926 "WHERE history_id = ?1 "
927 "LIMIT 1;";
928
929 DbStatement stmt = DbStatement::Prepare(db, sql, history_id);
930
931 RawTraceModel p{db};
932 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
933 return std::nullopt;
934 }
935
936 return p;
937 }
938
Igor Murashkin5e216982019-10-17 14:11:44 -0700939 static std::optional<RawTraceModel> Insert(DbHandle db,
940 int history_id,
941 std::string file_path) {
942 const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);";
943
944 std::optional<int> inserted_row_id =
945 DbQueryBuilder::Insert(db, sql, history_id, file_path);
946 if (!inserted_row_id) {
947 return std::nullopt;
948 }
949
950 RawTraceModel p{db};
951 p.id = *inserted_row_id;
952 p.history_id = history_id;
953 p.file_path = file_path;
954
955 return p;
956 }
957
958 bool Delete() {
959 const char* sql = "DELETE FROM raw_traces WHERE id = ?";
960
961 return DbQueryBuilder::Delete(db(), sql, id);
962 }
963
964 int id;
965 int history_id;
966 std::string file_path;
967};
968
969inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) {
970 os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ",";
971 os << "file_path=" << p.file_path << "}";
972 return os;
973}
974
Yan Wangcdb97fa2019-10-18 18:18:48 -0700975class PrefetchFileModel : public Model {
976 protected:
977 PrefetchFileModel(DbHandle db) : Model{db} {
978 }
979
980 public:
981 static std::optional<PrefetchFileModel> Insert(DbHandle db,
982 int activity_id,
983 std::string file_path) {
984 const char* sql = "INSERT INTO prefetch_files (activity_id, file_path) VALUES (?1, ?2);";
985
986 std::optional<int> inserted_row_id =
987 DbQueryBuilder::Insert(db, sql, activity_id, file_path);
988 if (!inserted_row_id) {
989 return std::nullopt;
990 }
991
992 PrefetchFileModel p{db};
993 p.id = *inserted_row_id;
994 p.activity_id = activity_id;
995 p.file_path = file_path;
996
997 return p;
998 }
999
1000 bool Delete() {
1001 const char* sql = "DELETE FROM prefetch_files WHERE id = ?";
1002
1003 return DbQueryBuilder::Delete(db(), sql, id);
1004 }
1005
1006 int id;
1007 int activity_id;
1008 std::string file_path;
1009};
1010
1011inline std::ostream& operator<<(std::ostream& os, const PrefetchFileModel& p) {
1012 os << "PrefetchFileModel{id=" << p.id << ",activity_id=" << p.activity_id << ",";
1013 os << "file_path=" << p.file_path << "}";
1014 return os;
1015}
1016
Igor Murashkin03e5b052019-10-03 16:39:50 -07001017} // namespace iorap::db
1018
1019#endif // IORAP_SRC_DB_MODELS_H_