| // 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 "benchmarks.h" |
| |
| #include <inttypes.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/trace-engine/buffer_internal.h> |
| #include <lib/trace-engine/instrumentation.h> |
| #include <lib/trace/event.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #include <fbl/function.h> |
| #include <trace-vthread/event_vthread.h> |
| |
| #include "handler.h" |
| #include "runner.h" |
| |
| namespace { |
| |
| using Benchmark = fbl::Function<void()>; |
| |
| class Runner { |
| public: |
| Runner(bool enabled, const BenchmarkSpec* spec) : enabled_(enabled), spec_(spec) {} |
| |
| void Run(const char* name, Benchmark benchmark) { |
| if (enabled_) { |
| // The trace engine needs to run in its own thread in order to |
| // process buffer full requests in streaming mode while the |
| // benchmark is running. Note that records will still get lost |
| // if the engine thread is not scheduled frequently enough. This |
| // is a stress test so all the app is doing is filling the trace |
| // buffer. :-) |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| BenchmarkHandler handler(&loop, spec_->mode, spec_->buffer_size); |
| |
| loop.StartThread("trace-engine loop", nullptr); |
| |
| RunAndMeasure( |
| name, spec_->name, spec_->num_iterations, benchmark, [&handler]() { handler.Start(); }, |
| [&handler]() { handler.Stop(); }); |
| |
| loop.Quit(); |
| loop.JoinThreads(); |
| } else { |
| // For the disabled benchmarks we just use the default number |
| // of iterations. |
| RunAndMeasure( |
| name, spec_->name, benchmark, []() {}, []() {}); |
| } |
| } |
| |
| // Utility to print a line of text in the same format as RunAndMeasure. |
| void Print(const char* fmt, ...) { |
| fputs(kTestOutputPrefix, stdout); |
| va_list args; |
| va_start(args, fmt); |
| vprintf(fmt, args); |
| va_end(args); |
| } |
| |
| private: |
| const bool enabled_; |
| // nullptr if |!enabled_|. |
| const BenchmarkSpec* spec_; |
| }; |
| |
| } // namespace |
| |
| #define MAKE_TEST_SYMBOL_NAME(prefix, DURATION_MACRO, test_symbol_name, category) \ |
| prefix##DURATION_MACRO##test_symbol_name##category |
| |
| // clang-format doesn't understand the leading # |
| // clang-format off |
| #define MAKE_TEST_SYMBOL_STRING(prefix, DURATION_MACRO, test_symbol_name, category) \ |
| #prefix #DURATION_MACRO #test_symbol_name #category |
| // clang-format on |
| |
| // N.B. In order for this to work, this code cannot live in the anonymous |
| // namespace. Otherwise the compiler attaches that namespace to the section |
| // name which will no longer be a valid C symbol (which is what we need in |
| // order for the linker to generate the __{start,stop}_SECNAME symbols. |
| #define RUN_TEST(test_name, pretty_test_name, macro_name, enabled_prefix, category, expression) \ |
| do { \ |
| const char* full_test_name = #macro_name " macro with " pretty_test_name ": " #category; \ |
| struct Test { \ |
| static void Run() \ |
| __attribute__((__section__(MAKE_TEST_SYMBOL_STRING(test_, macro_name, test_name, \ |
| category)))) { \ |
| expression; \ |
| } \ |
| }; \ |
| runner.Run(full_test_name, Test::Run); \ |
| extern const char MAKE_TEST_SYMBOL_NAME(__start_test_, macro_name, test_name, category)[]; \ |
| extern const char MAKE_TEST_SYMBOL_NAME(__stop_test_, macro_name, test_name, category)[]; \ |
| size_t test_size = (MAKE_TEST_SYMBOL_NAME(__stop_test_, macro_name, test_name, category) - \ |
| MAKE_TEST_SYMBOL_NAME(__start_test_, macro_name, test_name, category)); \ |
| runner.Print("test size: %zu bytes\n", test_size); \ |
| } while (0) |
| |
| // The niladic version is needed to portably work around the trailing comma |
| // problem when using __VA_ARGS__. |
| #define RUN_NILADIC_DURATION_TEST(test_name, pretty_test_name, DURATION_MACRO, enabled_prefix, \ |
| category) \ |
| RUN_TEST(test_name, pretty_test_name, DURATION_MACRO, enabled_prefix, category, \ |
| DURATION_MACRO(enabled_prefix #category, "name")) |
| |
| #define RUN_DURATION_TEST(test_name, pretty_test_name, DURATION_MACRO, enabled_prefix, category, \ |
| ...) \ |
| RUN_TEST(test_name, pretty_test_name, DURATION_MACRO, enabled_prefix, category, \ |
| DURATION_MACRO(enabled_prefix #category, "name", __VA_ARGS__)) |
| |
| #define DURATION_TEST(DURATION_MACRO, enabled_prefix, category) \ |
| RUN_NILADIC_DURATION_TEST(zero_args, "0 arguments", DURATION_MACRO, enabled_prefix, category); \ |
| \ |
| RUN_DURATION_TEST(one_int32_arg, "1 int32 argument", DURATION_MACRO, enabled_prefix, category, \ |
| "k1", 1); \ |
| \ |
| RUN_DURATION_TEST(one_double_arg, "1 double argument", DURATION_MACRO, enabled_prefix, category, \ |
| "k1", 1.); \ |
| \ |
| RUN_DURATION_TEST(one_string_arg, "1 string argument", DURATION_MACRO, enabled_prefix, category, \ |
| "k1", "string1"); \ |
| \ |
| RUN_DURATION_TEST(four_int32_args, "4 int32 arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", 1, "k2", 2, "k3", 3, "k4", 4); \ |
| \ |
| RUN_DURATION_TEST(four_double_args, "4 double arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", 1., "k2", 2., "k3", 3., "k4", 4.); \ |
| \ |
| RUN_DURATION_TEST(four_string_args, "4 string arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", "string1", "k2", "string2", "k3", "string3", "k4", "string4"); \ |
| \ |
| RUN_DURATION_TEST(eight_int32_args, "8 int32 arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", 1, "k2", 2, "k3", 3, "k4", 4, "k5", 5, "k6", 6, "k7", 7, "k8", \ |
| 8); \ |
| \ |
| RUN_DURATION_TEST(eight_double_args, "8 double arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", 1., "k2", 2., "k3", 3., "k4", 4., "k5", 4., "k6", 5., "k7", \ |
| 7., "k8", 8.); \ |
| \ |
| RUN_DURATION_TEST(eight_string_args, "8 string arguments", DURATION_MACRO, enabled_prefix, \ |
| category, "k1", "string1", "k2", "string2", "k3", "string3", "k4", "string4", \ |
| "k5", "string5", "k6", "string6", "k7", "string7", "k8", "string8"); |
| |
| // This is not in the anonymous namespace so that the namespace name |
| // doesn't enter into the computation of the location of Test::Run which |
| // is put in its own section. |
| static void RunBenchmarks(bool tracing_enabled, const BenchmarkSpec* spec) { |
| Runner runner(tracing_enabled, spec); |
| |
| runner.Run("is enabled", [] { trace_is_enabled(); }); |
| |
| runner.Run("is category enabled", [] { trace_is_category_enabled("+enabled"); }); |
| |
| if (tracing_enabled) { |
| runner.Run("is category enabled for disabled category", |
| [] { trace_is_category_enabled("-disabled"); }); |
| } |
| |
| runner.Run("acquire / release context", [] { |
| trace_context_t* context = trace_acquire_context(); |
| if (unlikely(context)) |
| trace_release_context(context); |
| }); |
| |
| runner.Run("acquire / release context for category", [] { |
| trace_string_ref_t category_ref; |
| trace_context_t* context = trace_acquire_context_for_category("+enabled", &category_ref); |
| if (unlikely(context)) |
| trace_release_context(context); |
| }); |
| |
| if (tracing_enabled) { |
| runner.Run("acquire / release context for disabled category", [] { |
| trace_string_ref_t category_ref; |
| trace_context_t* context = trace_acquire_context_for_category("-disabled", &category_ref); |
| ZX_DEBUG_ASSERT(!context); |
| }); |
| } |
| |
| DURATION_TEST(TRACE_DURATION_BEGIN, "+", enabled); |
| DURATION_TEST(TRACE_DURATION, "+", enabled); |
| |
| // There's no real need (yet) to test vthread support with multiple |
| // variations of arguments. If we did that for all macros the S/N ratio |
| // of the output would drop too much. |
| RUN_TEST(zero_args, "0 arguments", TRACE_VTHREAD_DURATION_BEGIN, "+", enabled, |
| TRACE_VTHREAD_DURATION_BEGIN("+enabled", "name", "vthread", 1, zx_ticks_get())); |
| |
| if (tracing_enabled) { |
| DURATION_TEST(TRACE_DURATION_BEGIN, "-", disabled); |
| DURATION_TEST(TRACE_DURATION, "-", disabled); |
| |
| RUN_TEST(zero_args, "0 arguments", TRACE_VTHREAD_DURATION_BEGIN, "-", disabled, |
| TRACE_VTHREAD_DURATION_BEGIN("+enabled", "name", "vthread", 1, zx_ticks_get())); |
| } |
| } |
| |
| void RunTracingDisabledBenchmarks() { |
| static const BenchmarkSpec spec = { |
| "tracing off", |
| TRACE_BUFFERING_MODE_ONESHOT, // unused |
| 0, |
| kDefaultRunIterations, |
| }; |
| RunBenchmarks(false, &spec); |
| } |
| |
| void RunTracingEnabledBenchmarks(const BenchmarkSpec* spec) { RunBenchmarks(true, spec); } |