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