blob: 24984fa3afda6545d63da01b8ff449a8cfd42ca4 [file] [log] [blame]
// Copyright 2018 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.
#ifndef SRC_BRINGUP_BIN_TRACE_BENCHMARK_HANDLER_H_
#define SRC_BRINGUP_BIN_TRACE_BENCHMARK_HANDLER_H_
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/trace-provider/handler.h>
#include <lib/zx/event.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <fbl/array.h>
class BenchmarkHandler : public trace::TraceHandler {
public:
static constexpr int kWaitStoppedTimeoutSeconds = 10;
BenchmarkHandler(async::Loop* loop, trace_buffering_mode_t mode, size_t buffer_size)
: loop_(loop), mode_(mode), buffer_(new uint8_t[buffer_size], buffer_size) {
auto status = zx::event::create(0u, &observer_event_);
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "zx::event::create returned %s\n",
zx_status_get_string(status));
status = trace_register_observer(observer_event_.get());
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "trace_register_observer returned %s\n",
zx_status_get_string(status));
}
~BenchmarkHandler() {
auto status = trace_unregister_observer(observer_event_.get());
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "trace_unregister_observer returned %s\n",
zx_status_get_string(status));
}
trace_buffering_mode_t mode() const { return mode_; }
void Start() {
zx_status_t status =
trace_engine_initialize(loop_->dispatcher(), this, mode_, buffer_.data(), buffer_.size());
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "trace_engine_initialize returned %s\n",
zx_status_get_string(status));
status = trace_engine_start(TRACE_START_CLEAR_ENTIRE_BUFFER);
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "trace_engine_start returned %s\n",
zx_status_get_string(status));
ZX_DEBUG_ASSERT(trace_state() == TRACE_STARTED);
observer_event_.signal(ZX_EVENT_SIGNALED, 0u);
trace_notify_observer_updated(observer_event_.get());
}
void Stop() {
// Acquire the context before we stop. We can't after we stop
// as the context has likely been released (no more
// references).
trace::internal::trace_buffer_header header;
{
auto context = trace::TraceProlongedContext::Acquire();
trace_engine_terminate();
trace_context_snapshot_buffer_header_internal(context.get(), &header);
}
// Tracing hasn't actually stopped yet. It's stopping, but that won't
// complete until all context references are gone (which they are),
// and the engine has processed that fact (which it hasn't necessarily
// yet).
while (trace_state() != TRACE_STOPPED) {
auto status = observer_event_.wait_one(
ZX_EVENT_SIGNALED, zx::deadline_after(zx::sec(kWaitStoppedTimeoutSeconds)), nullptr);
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "observer_event_.wait_one returned %s\n",
zx_status_get_string(status));
observer_event_.signal(ZX_EVENT_SIGNALED, 0u);
}
if (mode_ == TRACE_BUFFERING_MODE_ONESHOT) {
ZX_DEBUG_ASSERT(header.wrapped_count == 0);
}
}
private:
bool IsCategoryEnabled(const char* category) override {
// Any category beginning with "+" is enabled.
return category[0] == '+';
}
void TraceStopped(zx_status_t disposition) override {
// This is noise if the status is ZX_OK, so just print if error.
// There's also no point in printing for ZX_ERR_NO_MEMORY, as that
// information can be determined from the number of records dropped.
if (disposition != ZX_OK && disposition != ZX_ERR_NO_MEMORY) {
printf("WARNING: Trace stopped, disposition = %s\n", zx_status_get_string(disposition));
}
if (mode_ == TRACE_BUFFERING_MODE_STREAMING) {
ZX_DEBUG_ASSERT(disposition == ZX_OK ||
// Some records could have been dropped while
// "saving" the buffer.
disposition == ZX_ERR_NO_MEMORY);
} else {
// In oneshot and circular modes we shouldn't have dropped
// any records.
ZX_DEBUG_ASSERT(disposition == ZX_OK);
}
}
void NotifyBufferFull(uint32_t wrapped_count, uint64_t durable_data_end) override {
// We shouldn't get this in oneshot or circular modes.
ZX_DEBUG_ASSERT(mode_ == TRACE_BUFFERING_MODE_STREAMING);
// The intent isn't to include buffer-save time in the benchmarks,
// so just immediately flag the buffer as saved. Alas since we're
// running on a separate thread records may get dropped. It depends on
// how well we're scheduled.
trace_engine_mark_buffer_saved(wrapped_count, durable_data_end);
}
async::Loop* const loop_;
const trace_buffering_mode_t mode_;
fbl::Array<uint8_t> const buffer_;
zx::event observer_event_;
};
#endif // SRC_BRINGUP_BIN_TRACE_BENCHMARK_HANDLER_H_