1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include <stdio.h> 8 9 #include <zircon/assert.h> 10 #include <zircon/status.h> 11 12 #include <fbl/array.h> 13 #include <lib/async-loop/cpp/loop.h> 14 #include <lib/zx/event.h> 15 #include <trace-provider/handler.h> 16 17 class BenchmarkHandler : public trace::TraceHandler { 18 public: 19 static constexpr int kWaitStoppedTimeoutSeconds = 10; 20 BenchmarkHandler(async::Loop * loop,trace_buffering_mode_t mode,size_t buffer_size)21 BenchmarkHandler(async::Loop* loop, trace_buffering_mode_t mode, 22 size_t buffer_size) 23 : loop_(loop), 24 mode_(mode), 25 buffer_(new uint8_t[buffer_size], buffer_size) { 26 auto status = zx::event::create(0u, &observer_event_); 27 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 28 "zx::event::create returned %s\n", 29 zx_status_get_string(status)); 30 status = trace_register_observer(observer_event_.get()); 31 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 32 "trace_register_observer returned %s\n", 33 zx_status_get_string(status)); 34 } 35 ~BenchmarkHandler()36 ~BenchmarkHandler() { 37 auto status = trace_unregister_observer(observer_event_.get()); 38 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 39 "trace_unregister_observer returned %s\n", 40 zx_status_get_string(status)); 41 } 42 mode()43 trace_buffering_mode_t mode() const { return mode_; } 44 Start()45 void Start() { 46 zx_status_t status = trace_start_engine(loop_->dispatcher(), 47 this, mode_, 48 buffer_.get(), buffer_.size()); 49 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 50 "trace_start_engine returned %s\n", 51 zx_status_get_string(status)); 52 ZX_DEBUG_ASSERT(trace_state() == TRACE_STARTED); 53 observer_event_.signal(ZX_EVENT_SIGNALED, 0u); 54 trace_notify_observer_updated(observer_event_.get()); 55 } 56 Stop()57 void Stop() { 58 // Acquire the context before we stop. We can't after we stop 59 // as the context has likely been released (no more 60 // references). 61 trace::internal::trace_buffer_header header; 62 { 63 auto context = trace::TraceProlongedContext::Acquire(); 64 auto status = trace_stop_engine(ZX_OK); 65 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 66 "trace_stop_engine returned %s\n", 67 zx_status_get_string(status)); 68 trace_context_snapshot_buffer_header(context.get(), &header); 69 } 70 71 // Tracing hasn't actually stopped yet. It's stopping, but that won't 72 // complete until all context references are gone (which they are), 73 // and the engine has processed that fact (which it hasn't necessarily 74 // yet). 75 while (trace_state() != TRACE_STOPPED) { 76 auto status = observer_event_.wait_one( 77 ZX_EVENT_SIGNALED, 78 zx::deadline_after(zx::sec(kWaitStoppedTimeoutSeconds)), 79 nullptr); 80 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, 81 "observer_event_.wait_one returned %s\n", 82 zx_status_get_string(status)); 83 observer_event_.signal(ZX_EVENT_SIGNALED, 0u); 84 } 85 86 if (mode_ == TRACE_BUFFERING_MODE_ONESHOT) { 87 ZX_DEBUG_ASSERT(header.wrapped_count == 0); 88 } 89 } 90 91 private: IsCategoryEnabled(const char * category)92 bool IsCategoryEnabled(const char* category) override { 93 // Any category beginning with "+" is enabled. 94 return category[0] == '+'; 95 } 96 TraceStopped(async_dispatcher_t * async,zx_status_t disposition,size_t buffer_bytes_written)97 void TraceStopped(async_dispatcher_t* async, 98 zx_status_t disposition, 99 size_t buffer_bytes_written) override { 100 // This is noise if the status is ZX_OK, so just print if error. 101 // There's also no point in printing for ZX_ERR_NO_MEMORY, as that 102 // information can be determined from the number of records dropped. 103 if (disposition != ZX_OK && disposition != ZX_ERR_NO_MEMORY) { 104 printf("WARNING: Trace stopped, disposition = %s\n", 105 zx_status_get_string(disposition)); 106 } 107 108 if (mode_ == TRACE_BUFFERING_MODE_STREAMING) { 109 ZX_DEBUG_ASSERT(disposition == ZX_OK || 110 // Some records could have been dropped while 111 // "saving" the buffer. 112 disposition == ZX_ERR_NO_MEMORY); 113 } else { 114 // In oneshot and circular modes we shouldn't have dropped 115 // any records. 116 ZX_DEBUG_ASSERT(disposition == ZX_OK); 117 } 118 } 119 NotifyBufferFull(uint32_t wrapped_count,uint64_t durable_data_end)120 void NotifyBufferFull(uint32_t wrapped_count, 121 uint64_t durable_data_end) override { 122 // We shouldn't get this in oneshot or circular modes. 123 ZX_DEBUG_ASSERT(mode_ == TRACE_BUFFERING_MODE_STREAMING); 124 125 // The intent isn't to include buffer-save time in the benchmarks, 126 // so just immediately flag the buffer as saved. Alas since we're 127 // running on a separate thread records may get dropped. It depends on 128 // how well we're scheduled. 129 trace_engine_mark_buffer_saved(wrapped_count, durable_data_end); 130 } 131 132 async::Loop* const loop_; 133 const trace_buffering_mode_t mode_; 134 fbl::Array<uint8_t> const buffer_; 135 zx::event observer_event_; 136 }; 137