blob: f2ff65196e933179eeb9c64a1bb0ddf0ce4ecfc3 [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
553 static std::optional<PackageModel> Insert(DbHandle db,
554 std::string name,
555 std::optional<int> version) {
556 const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
557
558 std::optional<int> inserted_row_id =
559 DbQueryBuilder::Insert(db, sql, name, version);
560 if (!inserted_row_id) {
561 return std::nullopt;
562 }
563
564 PackageModel p{db};
565 p.name = name;
566 p.version = version;
567 p.id = *inserted_row_id;
568
569 return p;
570 }
571
572 int id;
573 std::string name;
574 std::optional<int> version;
575};
576
577inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
578 os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
579 os << "version=";
580 if (p.version) {
581 os << *p.version;
582 } else {
583 os << "(nullopt)";
584 }
585 os << "}";
586 return os;
587}
588
589class ActivityModel : public Model {
590 protected:
591 ActivityModel(DbHandle db) : Model{db} {
592 }
593
594 public:
595 static std::optional<ActivityModel> SelectById(DbHandle db, int id) {
596 ScopedLockDb lock{db};
597 int original_id = id;
598
599 std::string query = "SELECT * FROM activities WHERE id = ? LIMIT 1;";
600 DbStatement stmt = DbStatement::Prepare(db, query, id);
601
602 ActivityModel p{db};
603 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
604 return std::nullopt;
605 }
606
607 return p;
608 }
609
610 static std::optional<ActivityModel> SelectByNameAndPackageId(DbHandle db,
611 const char* name,
612 int package_id) {
613 ScopedLockDb lock{db};
614
615 std::string query = "SELECT * FROM activities WHERE name = ? AND package_id = ? LIMIT 1;";
616 DbStatement stmt = DbStatement::Prepare(db, query, name, package_id);
617
618 ActivityModel p{db};
619 if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.package_id)) {
620 return std::nullopt;
621 }
622
623 return p;
624 }
625
626 static std::optional<ActivityModel> Insert(DbHandle db,
627 std::string name,
628 int package_id) {
629 const char* sql = "INSERT INTO activities (name, package_id) VALUES (?1, ?2);";
630
631 std::optional<int> inserted_row_id =
632 DbQueryBuilder::Insert(db, sql, name, package_id);
633 if (!inserted_row_id) {
634 return std::nullopt;
635 }
636
637 ActivityModel p{db};
638 p.id = *inserted_row_id;
639 p.name = name;
640 p.package_id = package_id;
641
642 return p;
643 }
644
645 // Try to select by package_name+activity_name, otherwise insert into both tables.
646 // Package version is ignored for selects.
647 static std::optional<ActivityModel> SelectOrInsert(
648 DbHandle db,
649 std::string package_name,
650 std::optional<int> package_version,
651 std::string activity_name) {
652 std::optional<PackageModel> package = PackageModel::SelectByName(db, package_name.c_str());
653 if (!package) {
654 package = PackageModel::Insert(db, package_name, package_version);
655 DCHECK(package.has_value());
656 }
657
658 std::optional<ActivityModel> activity =
659 ActivityModel::SelectByNameAndPackageId(db,
660 activity_name.c_str(),
661 package->id);
662 if (!activity) {
663 activity = Insert(db, activity_name, package->id);
664 // XX: should we really return an optional here? This feels like it should never fail.
665 }
666
667 return activity;
668 }
669
670 int id;
671 std::string name;
672 int package_id; // PackageModel::id
673};
674
675inline std::ostream& operator<<(std::ostream& os, const ActivityModel& p) {
676 os << "ActivityModel{id=" << p.id << ",name=" << p.name << ",";
677 os << "package_id=" << p.package_id << "}";
678 return os;
679}
680
681class AppLaunchHistoryModel : public Model {
682 protected:
683 AppLaunchHistoryModel(DbHandle db) : Model{db} {
684 }
685
686 public:
687 enum class Temperature : int32_t {
688 kUninitialized = -1, // Note: Not a valid SQL value.
689 kCold = 1,
690 kWarm = 2,
691 kHot = 3,
692 };
693
694 static std::optional<AppLaunchHistoryModel> SelectById(DbHandle db, int id) {
695 ScopedLockDb lock{db};
696 int original_id = id;
697
698 std::string query = "SELECT * FROM app_launch_histories WHERE id = ? LIMIT 1;";
699 DbStatement stmt = DbStatement::Prepare(db, query, id);
700
701 AppLaunchHistoryModel p{db};
702 if (!DbQueryBuilder::SelectOnce(stmt,
703 p.id,
704 p.activity_id,
705 p.temperature,
706 p.trace_enabled,
707 p.readahead_enabled,
708 p.total_time_ns,
709 p.report_fully_drawn_ns)) {
710 return std::nullopt;
711 }
712
713 return p;
714 }
715
716 static std::optional<AppLaunchHistoryModel> Insert(DbHandle db,
717 int activity_id,
718 AppLaunchHistoryModel::Temperature temperature,
719 bool trace_enabled,
720 bool readahead_enabled,
721 std::optional<uint64_t> total_time_ns,
722 std::optional<uint64_t> report_fully_drawn_ns)
723 {
724 const char* sql = "INSERT INTO app_launch_histories (activity_id, temperature, trace_enabled, "
725 "readahead_enabled, total_time_ns, "
726 "report_fully_drawn_ns) "
727 "VALUES (?1, ?2, ?3, ?4, ?5, ?6);";
728
729 std::optional<int> inserted_row_id =
730 DbQueryBuilder::Insert(db,
731 sql,
732 activity_id,
733 temperature,
734 trace_enabled,
735 readahead_enabled,
736 total_time_ns,
737 report_fully_drawn_ns);
738 if (!inserted_row_id) {
739 return std::nullopt;
740 }
741
742 AppLaunchHistoryModel p{db};
743 p.id = *inserted_row_id;
744 p.activity_id = activity_id;
745 p.temperature = temperature;
746 p.trace_enabled = trace_enabled;
747 p.readahead_enabled = readahead_enabled;
748 p.total_time_ns = total_time_ns;
749 p.report_fully_drawn_ns = report_fully_drawn_ns;
750
751 return p;
752 }
753
754 int id;
755 int activity_id; // ActivityModel::id
756 Temperature temperature = Temperature::kUninitialized;
757 bool trace_enabled;
758 bool readahead_enabled;
759 std::optional<uint64_t> total_time_ns;
760 std::optional<uint64_t> report_fully_drawn_ns;
761};
762
763inline std::ostream& operator<<(std::ostream& os, const AppLaunchHistoryModel& p) {
764 os << "AppLaunchHistoryModel{id=" << p.id << ","
765 << "activity_id=" << p.activity_id << ","
766 << "temperature=" << static_cast<int>(p.temperature) << ","
767 << "trace_enabled=" << p.trace_enabled << ","
768 << "readahead_enabled=" << p.readahead_enabled << ","
769 << "total_time_ns=";
770 if (p.total_time_ns) {
771 os << *p.total_time_ns;
772 } else {
773 os << "(nullopt)";
774 }
775 os << ",";
776 os << "report_fully_drawn_ns=";
777 if (p.report_fully_drawn_ns) {
778 os << *p.report_fully_drawn_ns;
779 } else {
780 os << "(nullopt)";
781 }
782 os << "}";
783 return os;
784}
785
Igor Murashkin5e216982019-10-17 14:11:44 -0700786class RawTraceModel : public Model {
787 protected:
788 RawTraceModel(DbHandle db) : Model{db} {
789 }
790
791 public:
792
793 // Return raw_traces, sorted ascending by the id.
794 static std::vector<RawTraceModel> SelectByPackageNameActivityName(DbHandle db,
795 std::string package_name,
796 std::string activity_name) {
797 ScopedLockDb lock{db};
798
799 const char* sql =
800 "SELECT raw_traces.id, raw_traces.history_id, raw_traces.file_path "
801 "FROM raw_traces "
802 "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
803 "INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
804 "INNER JOIN packages ON packages.id = activities.package_id "
805 "WHERE packages.name = ? AND activities.name = ? "
806 "ORDER BY raw_traces.id ASC";
807
808 DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
809
810 std::vector<RawTraceModel> results;
811
812 RawTraceModel p{db};
813 while (DbQueryBuilder::SelectOnce(stmt, p.id, p.history_id, p.file_path)) {
814 results.push_back(p);
815 }
816
817 return results;
818 }
819
820 static std::optional<RawTraceModel> Insert(DbHandle db,
821 int history_id,
822 std::string file_path) {
823 const char* sql = "INSERT INTO raw_traces (history_id, file_path) VALUES (?1, ?2);";
824
825 std::optional<int> inserted_row_id =
826 DbQueryBuilder::Insert(db, sql, history_id, file_path);
827 if (!inserted_row_id) {
828 return std::nullopt;
829 }
830
831 RawTraceModel p{db};
832 p.id = *inserted_row_id;
833 p.history_id = history_id;
834 p.file_path = file_path;
835
836 return p;
837 }
838
839 bool Delete() {
840 const char* sql = "DELETE FROM raw_traces WHERE id = ?";
841
842 return DbQueryBuilder::Delete(db(), sql, id);
843 }
844
845 int id;
846 int history_id;
847 std::string file_path;
848};
849
850inline std::ostream& operator<<(std::ostream& os, const RawTraceModel& p) {
851 os << "RawTraceModel{id=" << p.id << ",history_id=" << p.history_id << ",";
852 os << "file_path=" << p.file_path << "}";
853 return os;
854}
855
Igor Murashkin03e5b052019-10-03 16:39:50 -0700856} // namespace iorap::db
857
858#endif // IORAP_SRC_DB_MODELS_H_