cpp/writer/TraceLifecycleVisitor.cpp (148 lines of code) (raw):
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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 <errno.h>
#include <stdio.h>
#include <system_error>
#include <profilo/writer/DeltaEncodingVisitor.h>
#include <profilo/writer/PrintEntryVisitor.h>
#include <profilo/writer/StackTraceInvertingVisitor.h>
#include <profilo/writer/TimestampTruncatingVisitor.h>
#include <profilo/writer/TraceLifecycleVisitor.h>
namespace facebook {
namespace profilo {
namespace writer {
using namespace facebook::profilo::entries;
TraceLifecycleVisitor::TraceLifecycleVisitor(
const std::string& trace_folder,
const std::string& trace_prefix,
std::shared_ptr<TraceCallbacks> callbacks,
const std::vector<std::pair<std::string, std::string>>& headers,
int64_t trace_id,
std::function<void(TraceLifecycleVisitor& visitor)> trace_backward_callback)
:
trace_folder_(trace_folder),
trace_prefix_(trace_prefix),
trace_headers_(headers),
output_(nullptr),
delegates_(),
expected_trace_(trace_id),
callbacks_(callbacks),
started_(false),
done_(false),
trace_backward_callback_(std::move(trace_backward_callback)) {}
void TraceLifecycleVisitor::visit(const StandardEntry& entry) {
auto type = static_cast<EntryType>(entry.type);
switch (type) {
case EntryType::TRACE_END: {
int64_t trace_id = entry.extra;
if (trace_id != expected_trace_) {
return;
}
// write before we clean up state
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
onTraceEnd(trace_id);
break;
}
case EntryType::TRACE_TIMEOUT:
case EntryType::TRACE_ABORT: {
int64_t trace_id = entry.extra;
if (trace_id != expected_trace_) {
return;
}
auto reason = type == EntryType::TRACE_TIMEOUT
? AbortReason::TIMEOUT
: AbortReason::CONTROLLER_INITIATED;
// write before we clean up state
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
onTraceAbort(trace_id, reason);
break;
}
case EntryType::TRACE_BACKWARDS:
case EntryType::TRACE_START: {
int64_t trace_id = entry.extra;
if (trace_id != expected_trace_) {
return;
}
onTraceStart(trace_id, entry.matchid);
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
if (type == EntryType::TRACE_BACKWARDS) {
trace_backward_callback_(*this);
}
break;
}
case EntryType::LOGGER_PRIORITY: {
if (expected_trace_ == entry.extra) {
thread_priority_ = std::make_unique<ScopedThreadPriority>(entry.callid);
}
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
break;
}
default: {
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
}
}
}
void TraceLifecycleVisitor::visit(const FramesEntry& entry) {
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
}
void TraceLifecycleVisitor::visit(const BytesEntry& entry) {
if (hasDelegate()) {
delegates_.back()->visit(entry);
}
}
void TraceLifecycleVisitor::abort(AbortReason reason) {
onTraceAbort(expected_trace_, reason);
}
void TraceLifecycleVisitor::onTraceStart(int64_t trace_id, int32_t flags) {
if (output_ != nullptr) {
// active trace with same ID, abort
abort(AbortReason::NEW_START);
return;
}
output_ = TraceFileHelpers::openCompressedStream(
trace_id, trace_folder_, trace_prefix_);
TraceFileHelpers::writeHeaders(*output_, trace_id, trace_headers_);
// outputTime = truncate(current) - truncate(prev)
delegates_.emplace_back(new PrintEntryVisitor(*output_));
delegates_.emplace_back(new DeltaEncodingVisitor(*delegates_.back()));
delegates_.emplace_back(new TimestampTruncatingVisitor(
*delegates_.back(), TraceFileHelpers::kTimestampPrecision));
delegates_.emplace_back(new StackTraceInvertingVisitor(*delegates_.back()));
if (callbacks_.get() != nullptr) {
callbacks_->onTraceStart(trace_id, flags);
}
started_ = true;
done_ = false;
}
void TraceLifecycleVisitor::onTraceAbort(int64_t trace_id, AbortReason reason) {
done_ = true;
cleanupState();
if (started_ && callbacks_.get() != nullptr) {
callbacks_->onTraceAbort(trace_id, reason);
}
}
void TraceLifecycleVisitor::onTraceEnd(int64_t trace_id) {
done_ = true;
cleanupState();
if (started_ && callbacks_.get() != nullptr) {
callbacks_->onTraceEnd(trace_id);
}
}
void TraceLifecycleVisitor::cleanupState() {
delegates_.clear();
thread_priority_ = nullptr;
if (output_) {
output_->flush();
output_->close();
output_ = nullptr;
}
}
} // namespace writer
} // namespace profilo
} // namespace facebook