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