blob: 7baaea5150d39b1d75aef7870fee91a47c981c5f [file] [log] [blame]
// 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, &params);
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;
}