| // 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 <cmdline/args_parser.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <memory> |
| #include <thread> |
| |
| #include "src/developer/debug/debug_agent/debug_agent.h" |
| #include "src/developer/debug/debug_agent/socket_connection.h" |
| #include "src/developer/debug/debug_agent/unwind.h" |
| #include "src/developer/debug/shared/logging/logging.h" |
| #include "src/developer/debug/shared/message_loop_target.h" |
| #include "src/developer/debug/shared/zx_status.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| using namespace debug_ipc; |
| |
| namespace debug_agent { |
| namespace { |
| |
| // Valid options for the --unwind flag. |
| const char kAospUnwinder[] = "aosp"; |
| const char kNgUnwinder[] = "ng"; |
| |
| struct CommandLineOptions { |
| int port = 0; |
| bool debug_mode = false; |
| std::string unwind = kAospUnwinder; |
| }; |
| |
| const char kHelpIntro[] = R"(debug_agent --port=<port> [ <options> ] |
| |
| The debug_agent provides the on-device stub for the ZXDB frontend to talk |
| to. Once you launch the debug_agent, connect zxdb to the same port you |
| provide on the command-line. |
| |
| Options |
| |
| )"; |
| |
| const char kHelpHelp[] = R"( --help |
| -h |
| Prints all command-line switches.)"; |
| |
| const char kPortHelp[] = R"( --port=<port> |
| [Required] TCP port number to listen to incoming connections on.)"; |
| |
| const char kDebugModeHelp[] = R"( --debug-mode |
| -d |
| Run the agent on debug mode. This will enable conditional logging |
| messages and timing profiling. Mainly useful for people developing zxdb.)"; |
| |
| const char kUnwindHelp[] = R"( --unwind=[aosp|ng] |
| Force using either the AOSP or NG unwinder for generating stack traces.)"; |
| |
| cmdline::Status ParseCommandLine(int argc, const char* argv[], CommandLineOptions* options) { |
| cmdline::ArgsParser<CommandLineOptions> parser; |
| |
| parser.AddSwitch("port", 0, kPortHelp, &CommandLineOptions::port); |
| parser.AddSwitch("debug-mode", 'd', kDebugModeHelp, &CommandLineOptions::debug_mode); |
| parser.AddSwitch("unwind", 0, kUnwindHelp, &CommandLineOptions::unwind); |
| |
| // Special --help switch which doesn't exist in the options structure. |
| bool requested_help = false; |
| parser.AddGeneralSwitch("help", 'h', kHelpHelp, [&requested_help]() { requested_help = true; }); |
| |
| std::vector<std::string> params; |
| cmdline::Status status = parser.Parse(argc, argv, options, ¶ms); |
| if (status.has_error()) |
| return status; |
| |
| // Handle --help switch since we're the one that knows about the switches. |
| if (requested_help) |
| return cmdline::Status::Error(kHelpIntro + parser.GetHelp()); |
| |
| return cmdline::Status::Ok(); |
| } |
| |
| } // namespace |
| } // namespace debug_agent |
| |
| // main -------------------------------------------------------------------------------------------- |
| |
| int main(int argc, const char* argv[]) { |
| debug_agent::CommandLineOptions options; |
| cmdline::Status status = ParseCommandLine(argc, argv, &options); |
| if (status.has_error()) { |
| fprintf(stderr, "%s\n", status.error_message().c_str()); |
| return 1; |
| } |
| |
| // Decode the unwinder type. |
| if (options.unwind == debug_agent::kAospUnwinder) { |
| debug_agent::SetUnwinderType(debug_agent::UnwinderType::kAndroid); |
| } else if (options.unwind == debug_agent::kNgUnwinder) { |
| debug_agent::SetUnwinderType(debug_agent::UnwinderType::kNgUnwind); |
| } else { |
| fprintf(stderr, "Invalid option for --unwind. See debug_agent --help.\n"); |
| return 1; |
| } |
| |
| // TODO(donosoc): Do correct category setup. |
| debug_ipc::SetLogCategories({LogCategory::kAll}); |
| if (options.debug_mode) { |
| printf("Running the debug agent in debug mode.\n"); |
| debug_ipc::SetDebugMode(true); |
| } |
| |
| if (options.port) { |
| auto services = sys::ServiceDirectory::CreateFromNamespace(); |
| |
| auto message_loop = std::make_unique<MessageLoopTarget>(); |
| zx_status_t status = message_loop->InitTarget(); |
| if (status != ZX_OK) { |
| FXL_LOG(ERROR) << "Could not initialize message loop: " |
| << debug_ipc::ZxStatusToString(status); |
| } |
| |
| // The scope ensures the objects are destroyed before calling Cleanup on the MessageLoop. |
| { |
| debug_agent::SocketServer server; |
| if (!server.Init(options.port)) { |
| message_loop->Cleanup(); |
| return 1; |
| } |
| |
| // The debug agent is independent on whether it's connected or not. |
| // The following loop will attempt to patch a stream to the debug agent in order to enable |
| // communication. |
| debug_agent::DebugAgent debug_agent(services); |
| |
| while (true) { |
| // Start a new thread that will listen on a socket from an incoming connection from a |
| // client. In the meantime, the main thread will block waiting for something to be posted |
| // to the main thread. |
| // |
| // When the connection thread effectively receives a connection, it will post a task to the |
| // loop to create the agent and begin normal debugger operation. Once the application quits |
| // the loop, the code will clean the connection thread and create another or exit the loop, |
| // according to the current agent's configuration. |
| { |
| debug_agent::SocketServer::ConnectionConfig conn_config; |
| conn_config.message_loop = message_loop.get(); |
| conn_config.debug_agent = &debug_agent; |
| conn_config.port = options.port; |
| std::thread conn_thread(&debug_agent::SocketServer::Run, &server, std::move(conn_config)); |
| |
| message_loop->Run(); |
| |
| DEBUG_LOG(Agent) << "Joining connection thread."; |
| conn_thread.join(); |
| } |
| |
| // See if the debug agent was told to exit. |
| if (debug_agent.should_quit()) |
| break; |
| |
| // Prepare for another connection. |
| // The resources need to be freed on the message loop's thread. |
| server.Reset(); |
| } |
| } |
| message_loop->Cleanup(); |
| } else { |
| fprintf(stderr, "ERROR: --port=<port-number> required. See debug_agent --help.\n\n"); |
| return 1; |
| } |
| |
| // It's very useful to have a simple message that informs the debug agent |
| // exited successfully. |
| fprintf(stderr, "\rSee you, Space Cowboy...\r\n"); |
| return 0; |
| } |