/* * Copyright (C) 2019 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/raw_table.h" #include #include "src/trace_processor/ftrace_descriptors.h" #include "src/trace_processor/sqlite_utils.h" #include "perfetto/trace/ftrace/binder.pbzero.h" #include "perfetto/trace/ftrace/clk.pbzero.h" #include "perfetto/trace/ftrace/ftrace.pbzero.h" #include "perfetto/trace/ftrace/ftrace_event.pbzero.h" #include "perfetto/trace/ftrace/sched.pbzero.h" namespace perfetto { namespace trace_processor { RawTable::RawTable(sqlite3* db, const TraceStorage* storage) : storage_(storage) { auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) { auto* thiz = static_cast(sqlite3_user_data(ctx)); thiz->ToSystrace(ctx, argc, argv); }; sqlite3_create_function(db, "to_ftrace", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr, nullptr); } void RawTable::RegisterTable(sqlite3* db, const TraceStorage* storage) { Table::Register(db, storage, "raw"); } StorageSchema RawTable::CreateStorageSchema() { const auto& raw = storage_->raw_events(); return StorageSchema::Builder() .AddGenericNumericColumn("id", RowIdAccessor(TableId::kRawEvents)) .AddOrderedNumericColumn("ts", &raw.timestamps()) .AddStringColumn("name", &raw.name_ids(), &storage_->string_pool()) .AddNumericColumn("cpu", &raw.cpus()) .AddNumericColumn("utid", &raw.utids()) .AddNumericColumn("arg_set_id", &raw.arg_set_ids()) .Build({"name", "ts"}); } uint32_t RawTable::RowCount() { return static_cast(storage_->raw_events().raw_event_count()); } int RawTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) { info->estimated_cost = RowCount(); // Only the string columns are handled by SQLite info->order_by_consumed = true; size_t name_index = schema().ColumnIndexFromName("name"); for (size_t i = 0; i < qc.constraints().size(); i++) { info->omit[i] = qc.constraints()[i].iColumn != static_cast(name_index); } return SQLITE_OK; } void RawTable::FormatSystraceArgs(NullTermStringView event_name, ArgSetId arg_set_id, base::StringWriter* writer) { const auto& set_ids = storage_->args().set_ids(); auto lb = std::lower_bound(set_ids.begin(), set_ids.end(), arg_set_id); auto ub = std::find(lb, set_ids.end(), arg_set_id + 1); auto start_row = static_cast(std::distance(set_ids.begin(), lb)); using Variadic = TraceStorage::Args::Variadic; using ValueWriter = std::function; auto write_value = [this, writer](const Variadic& value) { switch (value.type) { case TraceStorage::Args::Variadic::kInt: writer->AppendInt(value.int_value); break; case TraceStorage::Args::Variadic::kReal: writer->AppendDouble(value.real_value); break; case TraceStorage::Args::Variadic::kString: { const auto& str = storage_->GetString(value.string_value); writer->AppendString(str.c_str(), str.size()); } } }; auto write_value_at_index = [this, start_row](uint32_t arg_idx, ValueWriter value_fn) { value_fn(storage_->args().arg_values()[start_row + arg_idx]); }; auto write_arg = [this, writer, start_row](uint32_t arg_idx, ValueWriter value_fn) { uint32_t arg_row = start_row + arg_idx; const auto& args = storage_->args(); const auto& key = storage_->GetString(args.keys()[arg_row]); const auto& value = args.arg_values()[arg_row]; writer->AppendChar(' '); writer->AppendString(key.c_str(), key.size()); writer->AppendChar('='); value_fn(value); }; if (event_name == "sched_switch") { using SS = protos::pbzero::SchedSwitchFtraceEvent; write_arg(SS::kPrevCommFieldNumber - 1, write_value); write_arg(SS::kPrevPidFieldNumber - 1, write_value); write_arg(SS::kPrevPrioFieldNumber - 1, write_value); write_arg(SS::kPrevStateFieldNumber - 1, [writer](const Variadic& value) { auto state = static_cast(value.int_value); writer->AppendString(ftrace_utils::TaskState(state).ToString().data()); }); writer->AppendLiteral(" ==>"); write_arg(SS::kNextCommFieldNumber - 1, write_value); write_arg(SS::kNextPidFieldNumber - 1, write_value); write_arg(SS::kNextPrioFieldNumber - 1, write_value); return; } else if (event_name == "sched_wakeup") { using SW = protos::pbzero::SchedWakeupFtraceEvent; write_arg(SW::kCommFieldNumber - 1, write_value); write_arg(SW::kPidFieldNumber - 1, write_value); write_arg(SW::kPrioFieldNumber - 1, write_value); write_arg(SW::kTargetCpuFieldNumber - 1, [writer](const Variadic& value) { writer->AppendPaddedInt<'0', 3>(value.int_value); }); return; } else if (event_name == "clock_set_rate") { // TODO(lalitm): this is a big hack but the best way to do this now. // Doing this requires overhauling how we deal with args by pushing them all // to an array and then reading back from that array. // We use the string "todo" as the name to stay consistent with old // trace_to_text print code. writer->AppendString(" todo"); write_arg(0 /* state */, write_value); write_arg(1 /* cpu_id */, write_value); return; } else if (event_name == "clk_set_rate") { using CSR = protos::pbzero::ClkSetRateFtraceEvent; writer->AppendLiteral(" "); write_value_at_index(CSR::kNameFieldNumber - 1, write_value); writer->AppendLiteral(" "); write_value_at_index(CSR::kRateFieldNumber - 1, write_value); return; } else if (event_name == "binder_transaction") { using BT = protos::pbzero::BinderTransactionFtraceEvent; writer->AppendString(" transaction="); write_value_at_index(BT::kDebugIdFieldNumber - 1, write_value); writer->AppendString(" dest_node="); write_value_at_index(BT::kTargetNodeFieldNumber - 1, write_value); writer->AppendString(" dest_proc="); write_value_at_index(BT::kToProcFieldNumber - 1, write_value); writer->AppendString(" dest_thread="); write_value_at_index(BT::kToThreadFieldNumber - 1, write_value); write_arg(BT::kReplyFieldNumber - 1, write_value); writer->AppendString(" flags=0x"); write_value_at_index( BT::kFlagsFieldNumber - 1, [writer](const Variadic& value) { writer->AppendHexInt(static_cast(value.int_value)); }); writer->AppendString(" code=0x"); write_value_at_index( BT::kCodeFieldNumber - 1, [writer](const Variadic& value) { writer->AppendHexInt(static_cast(value.int_value)); }); return; } else if (event_name == "binder_transaction_alloc_buf") { using BTAB = protos::pbzero::BinderTransactionAllocBufFtraceEvent; writer->AppendString(" transaction="); write_value_at_index(BTAB::kDebugIdFieldNumber - 1, write_value); write_arg(BTAB::kDataSizeFieldNumber - 1, write_value); write_arg(BTAB::kOffsetsSizeFieldNumber - 1, write_value); return; } else if (event_name == "binder_transaction_received") { using BTR = protos::pbzero::BinderTransactionReceivedFtraceEvent; writer->AppendString(" transaction="); write_value_at_index(BTR::kDebugIdFieldNumber - 1, write_value); return; } else if (event_name == "print") { using P = protos::pbzero::PrintFtraceEvent; uint32_t arg_row = start_row + P::kBufFieldNumber - 1; const auto& args = storage_->args(); const auto& value = args.arg_values()[arg_row]; const auto& str = storage_->GetString(value.string_value); // If the last character is a newline in a print, just drop it. auto chars_to_print = !str.empty() && str.c_str()[str.size() - 1] == '\n' ? str.size() - 1 : str.size(); writer->AppendChar(' '); writer->AppendString(str.c_str(), chars_to_print); return; } uint32_t arg = 0; for (auto it = lb; it != ub; it++) { write_arg(arg++, write_value); } } void RawTable::ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv) { if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_INTEGER) { sqlite3_result_error(ctx, "Usage: to_ftrace(id)", -1); return; } RowId row_id = sqlite3_value_int64(argv[0]); auto pair = TraceStorage::ParseRowId(row_id); PERFETTO_DCHECK(pair.first == TableId::kRawEvents); auto row = pair.second; const auto& raw_evts = storage_->raw_events(); UniqueTid utid = raw_evts.utids()[row]; const auto& thread = storage_->GetThread(utid); uint32_t tgid = 0; if (thread.upid.has_value()) { tgid = storage_->GetProcess(thread.upid.value()).pid; } const auto& name = storage_->GetString(thread.name_id); char line[4096]; base::StringWriter writer(line, sizeof(line)); ftrace_utils::FormatSystracePrefix(raw_evts.timestamps()[row], raw_evts.cpus()[row], thread.tid, tgid, base::StringView(name), &writer); const auto& event_name = storage_->GetString(raw_evts.name_ids()[row]); writer.AppendChar(' '); if (event_name == "print") { writer.AppendString("tracing_mark_write"); } else { writer.AppendString(event_name.c_str(), event_name.size()); } writer.AppendChar(':'); FormatSystraceArgs(event_name, raw_evts.arg_set_ids()[row], &writer); sqlite3_result_text(ctx, writer.CreateStringCopy(), -1, free); } } // namespace trace_processor } // namespace perfetto