blob: 64ad341fa74dc81286d03ef944c2295ff61d6288 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/lib/measure/duration.h"
#include "src/lib/fxl/logging.h"
namespace tracing {
namespace measure {
MeasureDuration::MeasureDuration(std::vector<DurationSpec> specs) : specs_(std::move(specs)) {}
bool MeasureDuration::Process(const trace::Record::Event& event) {
switch (event.type()) {
case trace::EventType::kAsyncBegin:
case trace::EventType::kFlowBegin:
return ProcessAsyncOrFlowBegin(event);
case trace::EventType::kAsyncEnd:
case trace::EventType::kFlowEnd:
return ProcessAsyncOrFlowEnd(event);
case trace::EventType::kDurationBegin:
return ProcessDurationBegin(event);
case trace::EventType::kDurationEnd:
return ProcessDurationEnd(event);
case trace::EventType::kDurationComplete:
return ProcessDurationComplete(event);
default:
return true;
}
}
MeasureDuration::PendingBeginKey MeasureDuration::MakeKey(const trace::Record::Event& event) {
PendingBeginKey key;
key.category = event.category;
key.name = event.name;
switch (event.type()) {
case trace::EventType::kAsyncBegin:
key.type = PendingBeginKey::Type::Async;
key.id = event.data.GetAsyncBegin().id;
break;
case trace::EventType::kAsyncEnd:
key.type = PendingBeginKey::Type::Async;
key.id = event.data.GetAsyncEnd().id;
break;
case trace::EventType::kFlowBegin:
key.type = PendingBeginKey::Type::Flow;
key.id = event.data.GetFlowBegin().id;
break;
case trace::EventType::kFlowEnd:
key.type = PendingBeginKey::Type::Flow;
key.id = event.data.GetFlowEnd().id;
break;
default:
FXL_NOTREACHED();
}
return key;
}
bool MeasureDuration::ProcessAsyncOrFlowBegin(const trace::Record::Event& event) {
const PendingBeginKey key = MakeKey(event);
if (pending_begins_.count(key)) {
FXL_LOG(WARNING) << "Ignoring a trace event: duplicate async or flow begin event";
return false;
}
pending_begins_[key] = event.timestamp;
return true;
}
bool MeasureDuration::ProcessAsyncOrFlowEnd(const trace::Record::Event& event) {
const PendingBeginKey key = MakeKey(event);
if (pending_begins_.count(key) == 0) {
FXL_LOG(WARNING) << "Ignoring a trace event: async or flow end not preceded by begin.";
return false;
}
const auto begin_timestamp = pending_begins_[key];
pending_begins_.erase(key);
for (const DurationSpec& spec : specs_) {
if (!EventMatchesSpec(event, spec.event)) {
continue;
}
AddResult(spec.common.id, begin_timestamp, event.timestamp);
}
return true;
}
bool MeasureDuration::ProcessDurationBegin(const trace::Record::Event& event) {
FXL_DCHECK(event.type() == trace::EventType::kDurationBegin);
duration_stacks_[event.process_thread].push(event.timestamp);
return true;
}
bool MeasureDuration::ProcessDurationEnd(const trace::Record::Event& event) {
FXL_DCHECK(event.type() == trace::EventType::kDurationEnd);
const auto key = event.process_thread;
if (duration_stacks_.count(key) == 0 || duration_stacks_[key].empty()) {
FXL_LOG(WARNING) << "Ignoring trace event " << event.category.c_str() << ":"
<< event.name.c_str() << " @" << event.timestamp
<< ": duration end not matched by a previous duration begin.";
return false;
}
const auto begin_timestamp = duration_stacks_[key].top();
duration_stacks_[key].pop();
if (duration_stacks_[key].empty()) {
duration_stacks_.erase(key);
}
for (const DurationSpec& spec : specs_) {
if (!EventMatchesSpec(event, spec.event)) {
continue;
}
AddResult(spec.common.id, begin_timestamp, event.timestamp);
}
return true;
}
bool MeasureDuration::ProcessDurationComplete(const trace::Record::Event& event) {
FXL_DCHECK(event.type() == trace::EventType::kDurationComplete);
for (const DurationSpec& spec : specs_) {
if (!EventMatchesSpec(event, spec.event)) {
continue;
}
AddResult(spec.common.id, event.timestamp, event.data.GetDurationComplete().end_time);
}
return true;
}
void MeasureDuration::AddResult(uint64_t spec_id, trace_ticks_t from, trace_ticks_t to) {
results_[spec_id].push_back(to - from);
}
bool MeasureDuration::PendingBeginKey::operator<(const PendingBeginKey& other) const {
if (type != other.type) {
return type < other.type;
}
if (category != other.category) {
return category < other.category;
}
if (name != other.name) {
return name < other.name;
}
return id < other.id;
}
} // namespace measure
} // namespace tracing