blob: 04977d801fb92d8702ac97d5b3b4b3beeedb8558 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/trace_processor.h"
#include <sqlite3.h>
#include <functional>
#include "perfetto/base/task_runner.h"
#include "src/trace_processor/process_table.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/sched_slice_table.h"
#include "src/trace_processor/sched_tracker.h"
#include "src/trace_processor/thread_table.h"
#include "src/trace_processor/trace_parser.h"
#include "perfetto/trace_processor/raw_query.pb.h"
namespace perfetto {
namespace trace_processor {
namespace {
constexpr uint32_t kTraceChunkSizeB = 16 * 1024 * 1024; // 16 MB
} // namespace
TraceProcessor::TraceProcessor(base::TaskRunner* task_runner)
: task_runner_(task_runner), weak_factory_(this) {
sqlite3* db = nullptr;
PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
db_.reset(std::move(db));
context_.sched_tracker.reset(new SchedTracker(&context_));
context_.process_tracker.reset(new ProcessTracker(&context_));
context_.storage.reset(new TraceStorage());
SchedSliceTable::RegisterTable(*db_, context_.storage.get());
ProcessTable::RegisterTable(*db_, context_.storage.get());
ThreadTable::RegisterTable(*db_, context_.storage.get());
}
TraceProcessor::~TraceProcessor() = default;
void TraceProcessor::LoadTrace(BlobReader* reader,
std::function<void()> callback) {
// Reset storage.
context_.storage->ResetStorage();
// Start a new trace parsing task.
context_.parser.reset(new TraceParser(reader, &context_, kTraceChunkSizeB));
LoadTraceChunk(callback);
}
void TraceProcessor::ExecuteQuery(
const protos::RawQueryArgs& args,
std::function<void(const protos::RawQueryResult&)> callback) {
protos::RawQueryResult proto;
const auto& sql = args.sql_query();
sqlite3_stmt* raw_stmt;
int err = sqlite3_prepare_v2(*db_, sql.c_str(), static_cast<int>(sql.size()),
&raw_stmt, nullptr);
ScopedStmt stmt(std::move(raw_stmt));
if (err) {
proto.set_error(sqlite3_errmsg(*db_));
callback(std::move(proto));
return;
}
int col_count = sqlite3_column_count(*stmt);
int row_count = 0;
for (int r = sqlite3_step(*stmt); r == SQLITE_ROW; r = sqlite3_step(*stmt)) {
for (int i = 0; i < col_count; i++) {
if (row_count == 0) {
// Setup the descriptors.
auto* descriptor = proto.add_column_descriptors();
descriptor->set_name(sqlite3_column_name(*stmt, i));
switch (sqlite3_column_type(*stmt, i)) {
case SQLITE_INTEGER:
descriptor->set_type(protos::RawQueryResult_ColumnDesc_Type_LONG);
break;
case SQLITE_TEXT:
descriptor->set_type(protos::RawQueryResult_ColumnDesc_Type_STRING);
break;
case SQLITE_FLOAT:
descriptor->set_type(protos::RawQueryResult_ColumnDesc_Type_DOUBLE);
break;
case SQLITE_NULL:
PERFETTO_CHECK(false);
break;
}
// Add an empty column.
proto.add_columns();
}
auto* column = proto.mutable_columns(i);
switch (proto.column_descriptors(i).type()) {
case protos::RawQueryResult_ColumnDesc_Type_LONG:
column->add_long_values(sqlite3_column_int64(*stmt, i));
break;
case protos::RawQueryResult_ColumnDesc_Type_STRING:
column->add_string_values(
reinterpret_cast<const char*>(sqlite3_column_text(*stmt, i)));
break;
case protos::RawQueryResult_ColumnDesc_Type_DOUBLE:
column->add_double_values(sqlite3_column_double(*stmt, i));
break;
}
}
row_count++;
}
proto.set_num_records(static_cast<uint64_t>(row_count));
callback(proto);
}
void TraceProcessor::LoadTraceChunk(std::function<void()> callback) {
bool has_more = context_.parser->ParseNextChunk();
if (!has_more) {
callback();
return;
}
auto weak_this = weak_factory_.GetWeakPtr();
task_runner_->PostTask([weak_this, callback] {
if (!weak_this)
return;
weak_this->LoadTraceChunk(callback);
});
}
} // namespace trace_processor
} // namespace perfetto