| // 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. |
| |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/cpp/time.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/trace-provider/provider.h> |
| #include <lib/trace/event.h> |
| #include <lib/trace/observer.h> |
| #include <lib/zx/time.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| |
| #include "src/lib/fxl/command_line.h" |
| #include "src/lib/fxl/log_settings_command_line.h" |
| #include "src/lib/fxl/strings/string_number_conversions.h" |
| |
| static constexpr int kDefaultCount = 1; |
| static constexpr int kDefaultDelaySeconds = 0; |
| static constexpr int kDefaultDurationSeconds = 10; |
| static constexpr int kWaitStartTimeoutSeconds = 60; |
| |
| static const char* prog_name; |
| |
| static void PrintHelp(FILE* f) { |
| fprintf(f, "Usage: %s [options]\n", prog_name); |
| fprintf(f, "Options:\n"); |
| fprintf(f, " --help Duh ...\n"); |
| fprintf(f, " --count=COUNT Specify number of records per iteration\n"); |
| fprintf(f, " The default is %d.\n", kDefaultCount); |
| fprintf(f, " --delay=SECONDS Delay SECONDS before starting\n"); |
| fprintf(f, " The default is %d.\n", kDefaultDelaySeconds); |
| fprintf(f, " --duration=SECONDS Specify time to run, in seconds\n"); |
| fprintf(f, " The default is %d.\n", kDefaultDurationSeconds); |
| fprintf(f, " --quiet[=LEVEL] Set quietness level (opposite of verbose)\n"); |
| fprintf(f, " --verbose[=LEVEL] Set debug verbosity level\n"); |
| } |
| |
| static void RunStressTestIteration(int count) { |
| // Simulate some kind of workload. |
| FX_LOGS(INFO) << "Doing work!"; |
| |
| static const char kSomethingCategory[] = "stress:something"; |
| static const char kWithZeroArgs[] = "with-zero-args"; |
| static const char kWithOneArg[] = "with-one-arg"; |
| static const char kWithTwoArgs[] = "with-two-args"; |
| |
| for (int i = 0; i < count; ++i) { |
| // Add some variety. |
| const char* event_name = nullptr; |
| switch (i % 3) { |
| case 0: |
| event_name = kWithZeroArgs; |
| TRACE_DURATION_BEGIN(kSomethingCategory, event_name); |
| break; |
| case 1: |
| event_name = kWithOneArg; |
| TRACE_DURATION_BEGIN(kSomethingCategory, event_name, "k1", 1); |
| break; |
| case 2: |
| event_name = kWithTwoArgs; |
| TRACE_DURATION_BEGIN(kSomethingCategory, event_name, "k1", 1, "k2", 2.0); |
| break; |
| default: |
| __UNREACHABLE; |
| } |
| zx::nanosleep(zx::deadline_after(zx::usec(1))); |
| TRACE_DURATION_END(kSomethingCategory, event_name); |
| } |
| } |
| |
| static bool WaitForTracingStarted(zx::duration timeout) { |
| // This implementation is more complex than it needs to be. |
| // We don't really need to use an async loop here. |
| // It is written this way as part of the purpose of this program is to |
| // provide examples of tracing usage. |
| trace::TraceObserver trace_observer; |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| |
| bool started = false; |
| auto on_trace_state_changed = [&loop, &started]() { |
| if (trace_state() == TRACE_STARTED) { |
| started = true; |
| } |
| // Any state change is relevant to us. If we're not started then we must |
| // have transitioned from STOPPED to STARTED to at least STOPPING. |
| loop.Quit(); |
| }; |
| |
| trace_observer.Start(loop.dispatcher(), std::move(on_trace_state_changed)); |
| if (trace_state() == TRACE_STARTED) { |
| return true; |
| } |
| |
| async::TaskClosure timeout_task([&loop] { |
| FX_LOGS(ERROR) << "Timed out waiting for tracing to start"; |
| loop.Quit(); |
| }); |
| timeout_task.PostDelayed(loop.dispatcher(), timeout); |
| loop.Run(); |
| |
| return started; |
| } |
| |
| int main(int argc, char** argv) { |
| prog_name = argv[0]; |
| auto cl = fxl::CommandLineFromArgcArgv(argc, argv); |
| if (!fxl::SetLogSettingsFromCommandLine(cl)) |
| return 1; |
| |
| if (cl.HasOption("help", nullptr)) { |
| PrintHelp(stdout); |
| return EXIT_SUCCESS; |
| } |
| |
| int count = kDefaultCount; |
| int delay = kDefaultDelaySeconds; |
| int duration = kDefaultDurationSeconds; |
| std::string arg; |
| |
| if (cl.GetOptionValue("count", &arg)) { |
| if (!fxl::StringToNumberWithError<int>(arg, &count) || count < 0) { |
| FX_LOGS(ERROR) << "Invalid count: " << arg; |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (cl.GetOptionValue("delay", &arg)) { |
| if (!fxl::StringToNumberWithError<int>(arg, &delay) || delay < 0) { |
| FX_LOGS(ERROR) << "Invalid delay: " << arg; |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (cl.GetOptionValue("duration", &arg)) { |
| if (!fxl::StringToNumberWithError<int>(arg, &duration) || duration < 0) { |
| FX_LOGS(ERROR) << "Invalid duration: " << arg; |
| return EXIT_FAILURE; |
| } |
| } |
| |
| // Use a separate loop for the provider. |
| // This is in anticipation of double-buffering support. |
| async::Loop provider_loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| provider_loop.StartThread("TraceProvider"); |
| std::unique_ptr<trace::TraceProviderWithFdio> provider; |
| bool already_started; |
| if (!trace::TraceProviderWithFdio::CreateSynchronously(provider_loop.dispatcher(), "trace_stress", |
| &provider, &already_started)) { |
| FX_LOGS(ERROR) << "Trace provider registration failed"; |
| return EXIT_FAILURE; |
| } |
| if (already_started) { |
| FX_LOGS(INFO) << "Tracing already started, waiting for our Start request"; |
| if (WaitForTracingStarted(zx::sec(kWaitStartTimeoutSeconds))) { |
| FX_LOGS(INFO) << "Start request received"; |
| } else { |
| FX_LOGS(WARNING) << "Error waiting for tracing to start, timed out"; |
| } |
| } |
| |
| if (delay > 0) { |
| FX_LOGS(INFO) << "Trace stressor delaying " << delay << " seconds ..."; |
| zx::nanosleep(zx::deadline_after(zx::sec(delay))); |
| } |
| |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| zx::time start_time = async::Now(loop.dispatcher()); |
| zx::time quit_time = start_time + zx::sec(duration); |
| |
| FX_LOGS(INFO) << "Trace stressor doing work for " << duration << " seconds ..."; |
| |
| int iteration = 0; |
| async::TaskClosure task([&loop, &task, &iteration, count, quit_time] { |
| TRACE_DURATION("stress:example", "Doing Work!", "iteration", ++iteration); |
| |
| RunStressTestIteration(count); |
| |
| zx::nanosleep(zx::deadline_after(zx::msec(500))); |
| |
| // Stop if quitting. |
| zx::time now = async::Now(loop.dispatcher()); |
| if (now > quit_time) { |
| loop.Quit(); |
| return; |
| } |
| |
| // Schedule more work in a little bit. |
| task.PostForTime(loop.dispatcher(), now + zx::msec(200)); |
| }); |
| task.PostForTime(loop.dispatcher(), start_time); |
| |
| loop.Run(); |
| |
| FX_LOGS(INFO) << "Trace stressor finished"; |
| |
| // Cleanly shutdown the provider thread. |
| provider_loop.Quit(); |
| provider_loop.JoinThreads(); |
| |
| return EXIT_SUCCESS; |
| } |