blob: fbffab8ca57eb93d2fa3d8704f2cd8db98a703fd [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 "garnet/public/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:
return ProcessAsyncStart(event);
case trace::EventType::kAsyncEnd:
return ProcessAsyncEnd(event);
case trace::EventType::kDurationBegin:
return ProcessDurationStart(event);
case trace::EventType::kDurationEnd:
return ProcessDurationEnd(event);
default:
return true;
}
}
bool MeasureDuration::ProcessAsyncStart(const trace::Record::Event& event) {
FXL_DCHECK(event.type() == trace::EventType::kAsyncBegin);
const PendingAsyncKey key = {event.category, event.name,
event.data.GetAsyncBegin().id};
if (pending_async_begins_.count(key)) {
FXL_LOG(WARNING) << "Ignoring a trace event: duplicate async begin event";
return false;
}
pending_async_begins_[key] = event.timestamp;
return true;
}
bool MeasureDuration::ProcessAsyncEnd(const trace::Record::Event& event) {
FXL_DCHECK(event.type() == trace::EventType::kAsyncEnd);
const PendingAsyncKey key = {event.category, event.name,
event.data.GetAsyncEnd().id};
if (pending_async_begins_.count(key) == 0) {
FXL_LOG(WARNING)
<< "Ignoring a trace event: async end not preceded by async begin.";
return false;
}
const auto begin_timestamp = pending_async_begins_[key];
pending_async_begins_.erase(key);
for (const DurationSpec& spec : specs_) {
if (!EventMatchesSpec(event, spec.event)) {
continue;
}
AddResult(spec.id, begin_timestamp, event.timestamp);
}
return true;
}
bool MeasureDuration::ProcessDurationStart(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 a trace event: 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.id, begin_timestamp, event.timestamp);
}
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::PendingAsyncKey::operator<(
const PendingAsyncKey& other) const {
if (category != other.category) {
return category < other.category;
}
if (name != other.name) {
return name < other.name;
}
return id < other.id;
}
} // namespace measure
} // namespace tracing