blob: 014306013617291c184d3ae4685695c224016bbf [file] [log] [blame]
/*
* 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/importers/fuchsia/fuchsia_trace_parser.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/flow_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
#include "src/trace_processor/importers/proto/proto_trace_parser.h"
namespace perfetto {
namespace trace_processor {
namespace {
// Record Types
constexpr uint32_t kEvent = 4;
// Event Types
constexpr uint32_t kInstant = 0;
constexpr uint32_t kCounter = 1;
constexpr uint32_t kDurationBegin = 2;
constexpr uint32_t kDurationEnd = 3;
constexpr uint32_t kDurationComplete = 4;
constexpr uint32_t kAsyncBegin = 5;
constexpr uint32_t kAsyncInstant = 6;
constexpr uint32_t kAsyncEnd = 7;
constexpr uint32_t kFlowBegin = 8;
constexpr uint32_t kFlowStep = 9;
constexpr uint32_t kFlowEnd = 10;
// Argument Types
constexpr uint32_t kNull = 0;
constexpr uint32_t kInt32 = 1;
constexpr uint32_t kUint32 = 2;
constexpr uint32_t kInt64 = 3;
constexpr uint32_t kUint64 = 4;
constexpr uint32_t kDouble = 5;
constexpr uint32_t kString = 6;
constexpr uint32_t kPointer = 7;
constexpr uint32_t kKoid = 8;
} // namespace
FuchsiaTraceParser::FuchsiaTraceParser(TraceProcessorContext* context)
: context_(context), proto_parser_(new ProtoTraceParser(context_)) {}
FuchsiaTraceParser::~FuchsiaTraceParser() = default;
void FuchsiaTraceParser::ParseTrackEvent(int64_t ts, TrackEventData data) {
proto_parser_->ParseTrackEvent(ts, std::move(data));
}
void FuchsiaTraceParser::ParseTracePacket(int64_t ts, TracePacketData data) {
proto_parser_->ParseTracePacket(ts, std::move(data));
}
std::optional<std::vector<FuchsiaTraceParser::Arg>>
FuchsiaTraceParser::ParseArgs(
fuchsia_trace_utils::RecordCursor& cursor,
uint32_t n_args,
std::function<StringId(base::StringView string)> intern_string,
std::function<StringId(uint32_t index)> get_string) {
std::vector<Arg> args;
for (uint32_t i = 0; i < n_args; i++) {
size_t arg_base = cursor.WordIndex();
uint64_t arg_header;
if (!cursor.ReadUint64(&arg_header)) {
return std::nullopt;
}
uint32_t arg_type =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
uint32_t arg_size_words =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 4, 15);
uint32_t arg_name_ref =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 16, 31);
Arg arg;
if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
base::StringView arg_name_view;
if (!cursor.ReadInlineString(arg_name_ref, &arg_name_view)) {
return std::nullopt;
}
arg.name = intern_string(arg_name_view);
} else {
arg.name = get_string(arg_name_ref);
}
switch (arg_type) {
case kNull:
arg.value = fuchsia_trace_utils::ArgValue::Null();
break;
case kInt32:
arg.value = fuchsia_trace_utils::ArgValue::Int32(
fuchsia_trace_utils::ReadField<int32_t>(arg_header, 32, 63));
break;
case kUint32:
arg.value = fuchsia_trace_utils::ArgValue::Uint32(
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 63));
break;
case kInt64: {
int64_t value;
if (!cursor.ReadInt64(&value)) {
return std::nullopt;
}
arg.value = fuchsia_trace_utils::ArgValue::Int64(value);
break;
}
case kUint64: {
uint64_t value;
if (!cursor.ReadUint64(&value)) {
return std::nullopt;
}
arg.value = fuchsia_trace_utils::ArgValue::Uint64(value);
break;
}
case kDouble: {
double value;
if (!cursor.ReadDouble(&value)) {
return std::nullopt;
}
arg.value = fuchsia_trace_utils::ArgValue::Double(value);
break;
}
case kString: {
uint32_t arg_value_ref =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 47);
StringId value;
if (fuchsia_trace_utils::IsInlineString(arg_value_ref)) {
base::StringView arg_value_view;
if (!cursor.ReadInlineString(arg_value_ref, &arg_value_view)) {
return std::nullopt;
}
value = intern_string(arg_value_view);
} else {
value = get_string(arg_value_ref);
}
arg.value = fuchsia_trace_utils::ArgValue::String(value);
break;
}
case kPointer: {
uint64_t value;
if (!cursor.ReadUint64(&value)) {
return std::nullopt;
}
arg.value = fuchsia_trace_utils::ArgValue::Pointer(value);
break;
}
case kKoid: {
uint64_t value;
if (!cursor.ReadUint64(&value)) {
return std::nullopt;
}
arg.value = fuchsia_trace_utils::ArgValue::Koid(value);
break;
}
default:
arg.value = fuchsia_trace_utils::ArgValue::Unknown();
break;
}
args.push_back(arg);
cursor.SetWordIndex(arg_base + arg_size_words);
}
return {std::move(args)};
}
void FuchsiaTraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord fr) {
// The timestamp is also present in the record, so we'll ignore the one
// passed as an argument.
fuchsia_trace_utils::RecordCursor cursor(fr.record_view()->data(),
fr.record_view()->length());
ProcessTracker* procs = context_->process_tracker.get();
SliceTracker* slices = context_->slice_tracker.get();
uint64_t header;
if (!cursor.ReadUint64(&header)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
uint32_t record_type = fuchsia_trace_utils::ReadField<uint32_t>(header, 0, 3);
switch (record_type) {
case kEvent: {
uint32_t event_type =
fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 19);
uint32_t n_args =
fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 23);
uint32_t thread_ref =
fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 31);
uint32_t cat_ref =
fuchsia_trace_utils::ReadField<uint32_t>(header, 32, 47);
uint32_t name_ref =
fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 63);
int64_t ts;
if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
FuchsiaThreadInfo tinfo;
if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
if (!cursor.ReadInlineThread(&tinfo)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
} else {
tinfo = fr.GetThread(thread_ref);
}
StringId cat;
if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
base::StringView cat_string_view;
if (!cursor.ReadInlineString(cat_ref, &cat_string_view)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
cat = context_->storage->InternString(cat_string_view);
} else {
cat = fr.GetString(cat_ref);
}
StringId name;
if (fuchsia_trace_utils::IsInlineString(name_ref)) {
base::StringView name_string_view;
if (!cursor.ReadInlineString(name_ref, &name_string_view)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
name = context_->storage->InternString(name_string_view);
} else {
name = fr.GetString(name_ref);
}
// Read arguments
const auto intern_string = [this](base::StringView string) {
return context_->storage->InternString(string);
};
const auto get_string = [&fr](uint32_t index) {
return fr.GetString(index);
};
auto maybe_args = FuchsiaTraceParser::ParseArgs(
cursor, n_args, intern_string, get_string);
if (!maybe_args.has_value()) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
auto insert_args =
[this, args = *maybe_args](ArgsTracker::BoundInserter* inserter) {
for (const Arg& arg : args) {
inserter->AddArg(
arg.name, arg.name,
arg.value.ToStorageVariadic(context_->storage.get()));
}
};
switch (event_type) {
case kInstant: {
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
break;
}
case kCounter: {
UniquePid upid =
procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
std::string name_str =
context_->storage->GetString(name).ToStdString();
uint64_t counter_id;
if (!cursor.ReadUint64(&counter_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
// Note: In the Fuchsia trace format, counter values are stored
// in the arguments for the record, with the data series defined
// by both the record name and the argument name. In Perfetto,
// counters only have one name, so we combine both names into
// one here.
for (const Arg& arg : *maybe_args) {
std::string counter_name_str = name_str + ":";
counter_name_str +=
context_->storage->GetString(arg.name).ToStdString();
counter_name_str += ":" + std::to_string(counter_id);
bool is_valid_value = false;
double counter_value = -1;
switch (arg.value.Type()) {
case fuchsia_trace_utils::ArgValue::kInt32:
is_valid_value = true;
counter_value = static_cast<double>(arg.value.Int32());
break;
case fuchsia_trace_utils::ArgValue::kUint32:
is_valid_value = true;
counter_value = static_cast<double>(arg.value.Uint32());
break;
case fuchsia_trace_utils::ArgValue::kInt64:
is_valid_value = true;
counter_value = static_cast<double>(arg.value.Int64());
break;
case fuchsia_trace_utils::ArgValue::kUint64:
is_valid_value = true;
counter_value = static_cast<double>(arg.value.Uint64());
break;
case fuchsia_trace_utils::ArgValue::kDouble:
is_valid_value = true;
counter_value = arg.value.Double();
break;
case fuchsia_trace_utils::ArgValue::kNull:
case fuchsia_trace_utils::ArgValue::kString:
case fuchsia_trace_utils::ArgValue::kPointer:
case fuchsia_trace_utils::ArgValue::kKoid:
case fuchsia_trace_utils::ArgValue::kUnknown:
context_->storage->IncrementStats(
stats::fuchsia_non_numeric_counters);
break;
}
if (is_valid_value) {
StringId counter_name_id = context_->storage->InternString(
base::StringView(counter_name_str));
TrackId track =
context_->track_tracker->InternProcessCounterTrack(
counter_name_id, upid);
context_->event_tracker->PushCounter(ts, counter_value, track);
}
}
break;
}
case kDurationBegin: {
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
slices->Begin(ts, track_id, cat, name, std::move(insert_args));
break;
}
case kDurationEnd: {
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
// TODO(b/131181693): |cat| and |name| are not passed here so
// that if two slices end at the same timestep, the slices get
// closed in the correct order regardless of which end event is
// processed first.
slices->End(ts, track_id, {}, {}, std::move(insert_args));
break;
}
case kDurationComplete: {
int64_t end_ts;
if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &end_ts)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
int64_t duration = end_ts - ts;
if (duration < 0) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
slices->Scoped(ts, track_id, cat, name, duration,
std::move(insert_args));
break;
}
case kAsyncBegin: {
int64_t correlation_id;
if (!cursor.ReadInt64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniquePid upid =
procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, upid, correlation_id);
slices->Begin(ts, track_id, cat, name, std::move(insert_args));
break;
}
case kAsyncInstant: {
int64_t correlation_id;
if (!cursor.ReadInt64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniquePid upid =
procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, upid, correlation_id);
slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
break;
}
case kAsyncEnd: {
int64_t correlation_id;
if (!cursor.ReadInt64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniquePid upid =
procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, upid, correlation_id);
slices->End(ts, track_id, cat, name, std::move(insert_args));
break;
}
case kFlowBegin: {
uint64_t correlation_id;
if (!cursor.ReadUint64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->flow_tracker->Begin(track_id, correlation_id);
break;
}
case kFlowStep: {
uint64_t correlation_id;
if (!cursor.ReadUint64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->flow_tracker->Step(track_id, correlation_id);
break;
}
case kFlowEnd: {
uint64_t correlation_id;
if (!cursor.ReadUint64(&correlation_id)) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
return;
}
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
static_cast<uint32_t>(tinfo.pid));
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->flow_tracker->End(track_id, correlation_id, true, true);
break;
}
}
break;
}
default: {
PERFETTO_DFATAL("Unknown record type %d in FuchsiaTraceParser",
record_type);
break;
}
}
}
} // namespace trace_processor
} // namespace perfetto