/hphp/runtime/debugger/debugger_client.cpp
C++ | 2564 lines | 2105 code | 286 blank | 173 comment | 450 complexity | 7a9a9ae2eb28f80e908e9b44e9b04305 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- +----------------------------------------------------------------------+
- | HipHop for PHP |
- +----------------------------------------------------------------------+
- | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- */
- #include "hphp/runtime/debugger/debugger_client.h"
- #include <signal.h>
- #include <fstream>
- #include "hphp/runtime/base/array-init.h"
- #include "hphp/runtime/base/array-iterator.h"
- #include "hphp/runtime/base/builtin-functions.h"
- #include "hphp/runtime/base/config.h"
- #include "hphp/runtime/base/preg.h"
- #include "hphp/runtime/base/program-functions.h"
- #include "hphp/runtime/base/string-util.h"
- #include "hphp/runtime/base/variable-serializer.h"
- #include "hphp/runtime/debugger/cmd/all.h"
- #include "hphp/runtime/debugger/debugger_command.h"
- #include "hphp/runtime/ext/sockets/ext_sockets.h"
- #include "hphp/runtime/ext/std/ext_std_network.h"
- #include "hphp/runtime/ext/string/ext_string.h"
- #include "hphp/runtime/vm/treadmill.h"
- #include "hphp/util/logger.h"
- #include "hphp/util/process.h"
- #include "hphp/util/stack-trace.h"
- #include "hphp/util/string-vsnprintf.h"
- #include "hphp/util/text-art.h"
- #include "hphp/util/text-color.h"
- #include <boost/scoped_ptr.hpp>
- #include <folly/Conv.h>
- #include <folly/portability/Unistd.h>
- #define USE_VARARGS
- #define PREFER_STDARG
- #ifdef USE_EDITLINE
- #include <editline/readline.h>
- #include <histedit.h>
- #else
- #include <readline/readline.h>
- #include <readline/history.h>
- #include <algorithm>
- #include <map>
- #include <memory>
- #include <set>
- #include <vector>
- #endif
- using namespace HPHP::TextArt;
- #define PHP_WORD_BREAK_CHARACTERS " \t\n\"\\'`@=;,|{[()]}+*%^!~&"
- namespace HPHP { namespace Eval {
- ///////////////////////////////////////////////////////////////////////////////
- TRACE_SET_MOD(debugger);
- static boost::scoped_ptr<DebuggerClient> debugger_client;
- const StaticString
- s_name("name"),
- s_cmds("cmds"),
- s_hhvm_never_save_config("hhvm.never_save_config");
- static String wordwrap(const String& str, int width /* = 75 */,
- const String& wordbreak /* = "\n" */,
- bool cut /* = false */) {
- Array args = Array::Create();
- args.append(str);
- args.append(width);
- args.append(wordbreak);
- args.append(cut);
- return vm_call_user_func("wordwrap", args).toString();
- }
- struct DebuggerExtension final : Extension {
- DebuggerExtension() : Extension("hhvm.debugger", NO_EXTENSION_VERSION_YET) {}
- } s_debugger_extension;
- static DebuggerClient& getStaticDebuggerClient() {
- TRACE(2, "DebuggerClient::getStaticDebuggerClient\n");
- /*
- * DebuggerClient acquires global mutexes in its constructor, so we
- * allocate debugger_client lazily to ensure that all of the
- * global mutexes have been initialized before we enter the
- * constructor.
- *
- * This initialization is thread-safe because program-functions.cpp
- * must call Debugger::StartClient (which ends up here) before any
- * additional threads are created.
- */
- if (!debugger_client) {
- debugger_client.reset(new DebuggerClient);
- }
- return *debugger_client;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // readline setups
- static char* debugger_generator(const char* text, int state) {
- TRACE(2, "DebuggerClient::debugger_generator\n");
- return getStaticDebuggerClient().getCompletion(text, state);
- }
- static char **debugger_completion(const char *text, int start, int end) {
- TRACE(2, "DebuggerClient::debugger_completion\n");
- if (getStaticDebuggerClient().setCompletion(text, start, end)) {
- return rl_completion_matches((char*)text, &debugger_generator);
- }
- return nullptr;
- }
- #ifndef USE_EDITLINE
- static rl_hook_func_t *old_rl_startup_hook = nullptr;
- static int saved_history_line_to_use = -1;
- static int last_saved_history_line = -1;
- static bool history_full() {
- return (history_is_stifled() && history_length >= history_max_entries);
- }
- static int set_saved_history() {
- if (history_full() && saved_history_line_to_use < history_length - 1) {
- saved_history_line_to_use++;
- }
- if (saved_history_line_to_use >= 0) {
- rl_get_previous_history(history_length - saved_history_line_to_use, 0);
- last_saved_history_line = saved_history_line_to_use;
- }
- saved_history_line_to_use = -1;
- rl_startup_hook = old_rl_startup_hook;
- return 0;
- }
- static int operate_and_get_next(int /*count*/, int c) {
- /* Accept the current line. */
- rl_newline (1, c);
- /* Find the current line, and find the next line to use. */
- int where = where_history();
- if (history_full() || (where >= history_length - 1)) {
- saved_history_line_to_use = where;
- } else {
- saved_history_line_to_use = where + 1;
- }
- old_rl_startup_hook = rl_startup_hook;
- rl_startup_hook = set_saved_history;
- return 0;
- }
- #endif
- static void debugger_signal_handler(int sig) {
- TRACE(2, "DebuggerClient::debugger_signal_handler\n");
- getStaticDebuggerClient().onSignal(sig);
- }
- void DebuggerClient::onSignal(int /*sig*/) {
- TRACE(2, "DebuggerClient::onSignal\n");
- if (m_inputState == TakingInterrupt) {
- if (m_sigCount == 0) {
- usageLogEvent("signal start");
- info("Pausing program execution, please wait...");
- } else if (m_sigCount == 1) {
- usageLogEvent("signal wait");
- help("Still attempting to pause program execution...");
- help(" Sometimes this takes a few seconds, so give it a chance,");
- help(" or press ctrl-c again to give up and terminate the debugger.");
- } else {
- usageLogEvent("signal quit");
- error("Debugger is quitting.");
- if (!getStaticDebuggerClient().isLocal()) {
- error(" Note: the program may still be running on the server.");
- }
- quit(); // NB: the machine is running, so can't send a real CmdQuit.
- return;
- }
- m_sigCount++;
- m_sigNum = CmdSignal::SignalBreak;
- } else {
- rl_line_buffer[0] = '\0';
- #ifndef USE_EDITLINE
- rl_free_line_state();
- rl_cleanup_after_signal();
- #endif
- rl_redisplay();
- }
- }
- int DebuggerClient::pollSignal() {
- TRACE(2, "DebuggerClient::pollSignal\n");
- if (m_scriptMode) {
- print(".....Debugger client still waiting for server response.....");
- }
- int ret = m_sigNum;
- m_sigNum = CmdSignal::SignalNone;
- return ret;
- }
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Initialization and shutdown.
- */
- struct ReadlineApp {
- ReadlineApp() {
- TRACE(2, "ReadlineApp::ReadlineApp\n");
- DebuggerClient::AdjustScreenMetrics();
- rl_attempted_completion_function = debugger_completion;
- rl_basic_word_break_characters = PHP_WORD_BREAK_CHARACTERS;
- #ifndef USE_EDITLINE
- rl_bind_keyseq("\\C-o", operate_and_get_next);
- rl_catch_signals = 0;
- #endif
- signal(SIGINT, debugger_signal_handler);
- TRACE(3, "ReadlineApp::ReadlineApp, about to call read_history\n");
- read_history((Process::GetHomeDirectory() +
- DebuggerClient::HistoryFileName).c_str());
- TRACE(3, "ReadlineApp::ReadlineApp, done calling read_history\n");
- }
- ~ReadlineApp() {
- TRACE(2, "ReadlineApp::~ReadlineApp\n");
- write_history((Process::GetHomeDirectory() +
- DebuggerClient::HistoryFileName).c_str());
- }
- };
- /**
- * Displaying a spinning wait icon.
- */
- struct ReadlineWaitCursor {
- ReadlineWaitCursor()
- : m_thread(this, &ReadlineWaitCursor::animate), m_waiting(true) {
- TRACE(2, "ReadlineWaitCursor::ReadlineWaitCursor\n");
- m_thread.start();
- }
- ~ReadlineWaitCursor() {
- TRACE(2, "ReadlineWaitCursor::~ReadlineWaitCursor\n");
- m_waiting = false;
- m_thread.waitForEnd();
- }
- void animate() {
- if (rl_point <= 0) return;
- auto p = rl_point - 1;
- auto orig = rl_line_buffer[p];
- while (m_waiting) {
- frame('|', p); frame('/', p); frame('-', p); frame('\\', p);
- rl_line_buffer[p] = orig;
- rl_redisplay();
- }
- }
- private:
- AsyncFunc<ReadlineWaitCursor> m_thread;
- bool m_waiting;
- void frame(char ch, int point) {
- rl_line_buffer[point] = ch;
- rl_redisplay();
- usleep(100000);
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- int DebuggerClient::LineWidth = 76;
- int DebuggerClient::CodeBlockSize = 20;
- int DebuggerClient::ScrollBlockSize = 20;
- const char *DebuggerClient::LineNoFormat = "%4d ";
- const char *DebuggerClient::LineNoFormatWithStar = "%4d*";
- const char *DebuggerClient::LocalPrompt = "hphpd";
- const char *DebuggerClient::ConfigFileName = ".hphpd.ini";
- const char *DebuggerClient::LegacyConfigFileName = ".hphpd.hdf";
- const char *DebuggerClient::HistoryFileName = ".hphpd.history";
- std::string DebuggerClient::HomePrefix = "/home";
- bool DebuggerClient::UseColor = true;
- bool DebuggerClient::NoPrompt = false;
- const char *DebuggerClient::HelpColor = nullptr;
- const char *DebuggerClient::InfoColor = nullptr;
- const char *DebuggerClient::OutputColor = nullptr;
- const char *DebuggerClient::ErrorColor = nullptr;
- const char *DebuggerClient::ItemNameColor = nullptr;
- const char *DebuggerClient::HighlightForeColor = nullptr;
- const char *DebuggerClient::HighlightBgColor = nullptr;
- const char *DebuggerClient::DefaultCodeColors[] = {
- /* None */ nullptr, nullptr,
- /* Keyword */ nullptr, nullptr,
- /* Comment */ nullptr, nullptr,
- /* String */ nullptr, nullptr,
- /* Variable */ nullptr, nullptr,
- /* Html */ nullptr, nullptr,
- /* Tag */ nullptr, nullptr,
- /* Declaration */ nullptr, nullptr,
- /* Constant */ nullptr, nullptr,
- /* LineNo */ nullptr, nullptr,
- };
- void DebuggerClient::LoadColors(const IniSetting::Map& ini, Hdf hdf) {
- TRACE(2, "DebuggerClient::LoadColors\n");
- HelpColor = LoadColor(ini, hdf, "Color.Help", "BROWN");
- InfoColor = LoadColor(ini, hdf, "Color.Info", "GREEN");
- OutputColor = LoadColor(ini, hdf, "Color.Output", "CYAN");
- ErrorColor = LoadColor(ini, hdf, "Color.Error", "RED");
- ItemNameColor = LoadColor(ini, hdf, "Color.ItemName", "GRAY");
- HighlightForeColor = LoadColor(ini, hdf, "Color.HighlightForeground", "RED");
- HighlightBgColor = LoadBgColor(ini, hdf, "Color.HighlightBackground", "GRAY");
- Hdf code = hdf["Code"];
- LoadCodeColor(CodeColorKeyword, ini, hdf, "Color.Code.Keyword",
- "CYAN");
- LoadCodeColor(CodeColorComment, ini, hdf, "Color.Code.Comment",
- "RED");
- LoadCodeColor(CodeColorString, ini, hdf, "Color.Code.String",
- "GREEN");
- LoadCodeColor(CodeColorVariable, ini, hdf, "Color.Code.Variable",
- "BROWN");
- LoadCodeColor(CodeColorHtml, ini, hdf, "Color.Code.Html",
- "GRAY");
- LoadCodeColor(CodeColorTag, ini, hdf, "Color.Code.Tag",
- "MAGENTA");
- LoadCodeColor(CodeColorDeclaration, ini, hdf, "Color.Code.Declaration",
- "BLUE");
- LoadCodeColor(CodeColorConstant, ini, hdf, "Color.Code.Constant",
- "MAGENTA");
- LoadCodeColor(CodeColorLineNo, ini, hdf, "Color.Code.LineNo",
- "GRAY");
- }
- const char *DebuggerClient::LoadColor(const IniSetting::Map& ini, Hdf hdf,
- const std::string& setting,
- const char *defaultName) {
- TRACE(2, "DebuggerClient::LoadColor\n");
- const char *name = Config::Get(ini, hdf, setting, defaultName);
- hdf = name; // for starter
- const char *color = get_color_by_name(name);
- if (color == nullptr) {
- Logger::Error("Bad color name %s", name);
- color = get_color_by_name(defaultName);
- }
- return color;
- }
- const char *DebuggerClient::LoadBgColor(const IniSetting::Map& ini, Hdf hdf,
- const std::string& setting,
- const char *defaultName) {
- TRACE(2, "DebuggerClient::LoadBgColor\n");
- const char *name = Config::Get(ini, hdf, setting, defaultName);
- hdf = name; // for starter
- const char *color = get_bgcolor_by_name(name);
- if (color == nullptr) {
- Logger::Error("Bad color name %s", name);
- color = get_bgcolor_by_name(defaultName);
- }
- return color;
- }
- void DebuggerClient::LoadCodeColor(CodeColor index, const IniSetting::Map& ini,
- Hdf hdf, const std::string& setting,
- const char *defaultName) {
- TRACE(2, "DebuggerClient::LoadCodeColor\n");
- const char *color = LoadColor(ini, hdf, setting, defaultName);
- DefaultCodeColors[index * 2] = color;
- DefaultCodeColors[index * 2 + 1] = color ? ANSI_COLOR_END : nullptr;
- }
- req::ptr<Socket> DebuggerClient::Start(const DebuggerClientOptions &options) {
- TRACE(2, "DebuggerClient::Start\n");
- auto ret = getStaticDebuggerClient().connectLocal();
- getStaticDebuggerClient().start(options);
- return ret;
- }
- void DebuggerClient::Stop() {
- TRACE(2, "DebuggerClient::Stop\n");
- if (debugger_client) {
- debugger_client.reset();
- }
- }
- void DebuggerClient::AdjustScreenMetrics() {
- TRACE(2, "entered: DebuggerClient::AdjustScreenMetrics\n");
- int rows = 0; int cols = 0;
- rl_get_screen_size(&rows, &cols);
- if (rows > 0 && cols > 0) {
- LineWidth = cols - 4;
- ScrollBlockSize = CodeBlockSize = rows - (rows >> 2);
- }
- TRACE(2, "leaving: DebuggerClient::AdjustScreenMetrics\n");
- }
- bool DebuggerClient::IsValidNumber(const std::string &arg) {
- TRACE(2, "DebuggerClient::IsValidNumber\n");
- if (arg.empty()) return false;
- for (auto c : arg) {
- if (!isdigit(c)) {
- return false;
- }
- }
- return true;
- }
- String DebuggerClient::FormatVariable(
- const Variant& v,
- char format /* = 'd' */
- ) {
- TRACE(2, "DebuggerClient::FormatVariable\n");
- String value;
- try {
- auto const t =
- format == 'r' ? VariableSerializer::Type::PrintR :
- format == 'v' ? VariableSerializer::Type::VarDump :
- VariableSerializer::Type::DebuggerDump;
- VariableSerializer vs(t, 0, 2);
- value = vs.serialize(v, true);
- } catch (const StringBufferLimitException& e) {
- value = "Serialization limit reached";
- } catch (...) {
- assertx(false);
- throw;
- }
- return value;
- }
- /*
- * Serializes a Variant, and truncates it to a limit if necessary. Returns the
- * truncated result, and the number of bytes truncated.
- */
- String DebuggerClient::FormatVariableWithLimit(const Variant& v, int maxlen) {
- assertx(maxlen >= 0);
- VariableSerializer vs(VariableSerializer::Type::DebuggerDump, 0, 2);
- auto const value = vs.serializeWithLimit(v, maxlen + 1);
- if (value.length() <= maxlen) {
- return value;
- }
- StringBuffer sb;
- sb.append(folly::StringPiece{value.data(), static_cast<size_t>(maxlen)});
- sb.append(" ...(omitted)");
- return sb.detach();
- }
- String DebuggerClient::FormatInfoVec(const IDebuggable::InfoVec &info,
- int *nameLen /* = NULL */) {
- TRACE(2, "DebuggerClient::FormatInfoVec\n");
- // vertical align names
- int maxlen = 0;
- for (unsigned int i = 0; i < info.size(); i++) {
- int len = strlen(info[i].first);
- if (len > maxlen) maxlen = len;
- }
- // print
- StringBuffer sb;
- for (unsigned int i = 0; i < info.size(); i++) {
- if (ItemNameColor) sb.append(ItemNameColor);
- std::string name = info[i].first;
- name += ": ";
- sb.append(name.substr(0, maxlen + 4));
- if (ItemNameColor) sb.append(ANSI_COLOR_END);
- if (OutputColor) sb.append(OutputColor);
- sb.append(info[i].second);
- if (OutputColor) sb.append(ANSI_COLOR_END);
- sb.append("\n");
- }
- if (nameLen) *nameLen = maxlen + 4;
- return sb.detach();
- }
- String DebuggerClient::FormatTitle(const char *title) {
- TRACE(2, "DebuggerClient::FormatTitle\n");
- String dash = HHVM_FN(str_repeat)(BOX_H, (LineWidth - strlen(title)) / 2 - 4);
- StringBuffer sb;
- sb.append("\n");
- sb.append(" ");
- sb.append(dash);
- sb.append(" "); sb.append(title); sb.append(" ");
- sb.append(dash);
- sb.append("\n");
- return sb.detach();
- }
- ///////////////////////////////////////////////////////////////////////////////
- DebuggerClient::DebuggerClient()
- : m_tutorial(0), m_scriptMode(false),
- m_logFile(""), m_logFileHandler(nullptr),
- m_mainThread(this, &DebuggerClient::run), m_stopped(false),
- m_inputState(TakingCommand),
- m_sigNum(CmdSignal::SignalNone), m_sigCount(0),
- m_acLen(0), m_acIndex(0), m_acPos(0), m_acLiveListsDirty(true),
- m_threadId(0), m_listLine(0), m_listLineFocus(0),
- m_frame(0),
- m_unknownCmd(false) {
- TRACE(2, "DebuggerClient::DebuggerClient\n");
- Debugger::InitUsageLogging();
- }
- DebuggerClient::~DebuggerClient() {
- TRACE(2, "DebuggerClient::~DebuggerClient\n");
- m_stopped = true;
- m_mainThread.waitForEnd();
- FILE *f = getLogFileHandler();
- if (f != nullptr) {
- fclose(f);
- setLogFileHandler(nullptr);
- }
- }
- void DebuggerClient::closeAllConnections() {
- TRACE(2, "DebuggerClient::closeAllConnections\n");
- for (unsigned int i = 0; i < m_machines.size(); i++) {
- m_machines[i]->m_thrift.close();
- }
- }
- bool DebuggerClient::isLocal() {
- TRACE(2, "DebuggerClient::isLocal\n");
- return m_machines[0] == m_machine;
- }
- bool DebuggerClient::connect(const std::string &host, int port) {
- TRACE(2, "DebuggerClient::connect\n");
- assertx((!m_machines.empty() && m_machines[0]->m_name == LocalPrompt));
- // First check for an existing connect, and reuse that.
- for (unsigned int i = 1; i < m_machines.size(); i++) {
- if (HHVM_FN(gethostbyname)(m_machines[i]->m_name) ==
- HHVM_FN(gethostbyname)(host)) {
- switchMachine(m_machines[i]);
- return false;
- }
- }
- return connectRemote(host, port);
- }
- bool DebuggerClient::connectRPC(const std::string &host, int port) {
- TRACE(2, "DebuggerClient::connectRPC\n");
- assertx(!m_machines.empty());
- auto local = m_machines[0];
- assertx(local->m_name == LocalPrompt);
- local->m_rpcHost = host;
- local->m_rpcPort = port;
- switchMachine(local);
- m_rpcHost = "rpc:" + host;
- usageLogEvent("RPC connect", m_rpcHost);
- return !local->m_interrupting;
- }
- bool DebuggerClient::disconnect() {
- TRACE(2, "DebuggerClient::disconnect\n");
- assertx(!m_machines.empty());
- auto local = m_machines[0];
- assertx(local->m_name == LocalPrompt);
- local->m_rpcHost.clear();
- local->m_rpcPort = 0;
- switchMachine(local);
- return !local->m_interrupting;
- }
- void DebuggerClient::switchMachine(std::shared_ptr<DMachineInfo> machine) {
- TRACE(2, "DebuggerClient::switchMachine\n");
- m_rpcHost.clear();
- machine->m_initialized = false; // even if m_machine == machine
- if (m_machine != machine) {
- m_machine = machine;
- m_sandboxes.clear();
- m_threads.clear();
- m_threadId = 0;
- m_breakpoint.reset();
- m_matched.clear();
- m_listFile.clear();
- m_listLine = 0;
- m_listLineFocus = 0;
- m_stacktrace.reset();
- m_frame = 0;
- }
- }
- req::ptr<Socket> DebuggerClient::connectLocal() {
- TRACE(2, "DebuggerClient::connectLocal\n");
- int fds[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
- throw Exception("unable to create socket pair for local debugging");
- }
- auto socket1 = req::make<StreamSocket>(fds[0], AF_UNIX);
- auto socket2 = req::make<StreamSocket>(fds[1], AF_UNIX);
- socket1->unregister();
- socket2->unregister();
- auto machine = std::make_shared<DMachineInfo>();
- machine->m_sandboxAttached = true;
- machine->m_name = LocalPrompt;
- machine->m_thrift.create(socket1);
- assertx(m_machines.empty());
- m_machines.push_back(machine);
- switchMachine(machine);
- return socket2;
- }
- bool DebuggerClient::connectRemote(const std::string &host, int port) {
- TRACE(2, "DebuggerClient::connectRemote\n");
- if (port <= 0) {
- port = RuntimeOption::DebuggerServerPort;
- }
- info("Connecting to %s:%d...", host.c_str(), port);
- if (tryConnect(host, port, false)) {
- return true;
- }
- error("Unable to connect to %s:%d.", host.c_str(), port);
- return false;
- }
- bool DebuggerClient::reconnect() {
- TRACE(2, "DebuggerClient::reconnect\n");
- assertx(m_machine);
- auto& host = m_machine->m_name;
- int port = m_machine->m_port;
- if (port <= 0) {
- return false;
- }
- info("Re-connecting to %s:%d...", host.c_str(), port);
- m_machine->m_thrift.close(); // Close the old socket, it may still be open.
- if (tryConnect(host, port, true)) {
- return true;
- }
- error("Still unable to connect to %s:%d.", host.c_str(), port);
- return false;
- }
- bool DebuggerClient::tryConnect(const std::string &host, int port,
- bool clearmachines) {
- struct addrinfo *ai;
- struct addrinfo hint;
- memset(&hint, 0, sizeof(hint));
- hint.ai_family = AF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- if (RuntimeOption::DebuggerDisableIPv6) {
- hint.ai_family = AF_INET;
- }
- if (getaddrinfo(host.c_str(), nullptr, &hint, &ai)) {
- return false;
- }
- SCOPE_EXIT {
- freeaddrinfo(ai);
- };
- /* try possible families (v4, v6) until we get a connection */
- struct addrinfo *cur;
- for (cur = ai; cur; cur = cur->ai_next) {
- auto sock = req::make<StreamSocket>(
- socket(cur->ai_family, cur->ai_socktype, 0),
- cur->ai_family,
- cur->ai_addr->sa_data,
- port
- );
- sock->unregister();
- if (HHVM_FN(socket_connect)(Resource(sock), String(host), port)) {
- if (clearmachines) {
- for (unsigned int i = 0; i < m_machines.size(); i++) {
- if (m_machines[i] == m_machine) {
- m_machines.erase(m_machines.begin() + i);
- break;
- }
- }
- }
- auto machine = std::make_shared<DMachineInfo>();
- machine->m_name = host;
- machine->m_port = port;
- machine->m_thrift.create(sock);
- m_machines.push_back(machine);
- switchMachine(machine);
- return true;
- }
- }
- return false;
- }
- std::string DebuggerClient::getPrompt() {
- TRACE(2, "DebuggerClient::getPrompt\n");
- if (NoPrompt || !RuntimeOption::EnableDebuggerPrompt) {
- return "";
- }
- auto name = &m_machine->m_name;
- if (!m_rpcHost.empty()) {
- name = &m_rpcHost;
- }
- if (m_inputState == TakingCode) {
- std::string prompt = " ";
- for (unsigned i = 2; i < name->size() + 2; i++) {
- prompt += '.';
- }
- prompt += ' ';
- return prompt;
- }
- return *name + "> ";
- }
- void DebuggerClient::init(const DebuggerClientOptions &options) {
- TRACE(2, "DebuggerClient::init\n");
- m_options = options;
- if (!options.configFName.empty()) {
- m_configFileName = options.configFName;
- }
- if (options.user.empty()) {
- m_options.user = Process::GetCurrentUser();
- }
- usageLogEvent("init");
- loadConfig();
- if (m_scriptMode) {
- print("running in script mode, pid=%" PRId64 "\n",
- (int64_t)getpid());
- }
- if (!options.cmds.empty()) {
- RuntimeOption::EnableDebuggerColor = false;
- RuntimeOption::EnableDebuggerPrompt = false;
- s_use_utf8 = false;
- }
- if (UseColor && RuntimeOption::EnableDebuggerColor) Debugger::SetTextColors();
- if (!NoPrompt && RuntimeOption::EnableDebuggerPrompt) {
- info("Welcome to HipHop Debugger!");
- info("Type \"help\" or \"?\" for a complete list of commands.\n");
- }
- if (!options.host.empty()) {
- connectRemote(options.host, options.port);
- } else {
- if (options.fileName.empty()) {
- help("Note: no server specified, debugging local scripts only.");
- help("If you want to connect to a server, launch with \"-h\" or use:");
- help(" [m]achine [c]onnect <servername>\n");
- }
- }
- }
- void DebuggerClient::start(const DebuggerClientOptions &options) {
- TRACE(2, "DebuggerClient::start\n");
- init(options);
- m_mainThread.start();
- }
- // Executed by m_mainThread to run the command-line debugger.
- void DebuggerClient::run() {
- TRACE(2, "DebuggerClient::run\n");
- StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
- ReadlineApp app;
- TRACE(3, "DebuggerClient::run, about to call playMacro\n");
- playMacro("startup");
- if (!m_options.cmds.empty()) {
- m_macroPlaying = std::make_shared<Macro>();
- m_macroPlaying->m_cmds = m_options.cmds;
- m_macroPlaying->m_cmds.push_back("q");
- m_macroPlaying->m_index = 0;
- }
- hphp_session_init(Treadmill::SessionKind::DebuggerClient);
- if (m_options.extension.empty()) {
- hphp_invoke_simple("", true); // warm-up only
- } else {
- hphp_invoke_simple(m_options.extension, false);
- }
- while (true) {
- bool reconnect = false;
- try {
- eventLoop(TopLevel, DebuggerCommand::KindOfNone, "Main client loop");
- } catch (DebuggerClientExitException& e) { /* normal exit */
- } catch (DebuggerServerLostException& e) {
- // Loss of connection
- TRACE_RB(1, "DebuggerClient::run: server lost exception\n");
- usageLogEvent("DebuggerServerLostException", m_commandCanonical);
- reconnect = true;
- } catch (DebuggerProtocolException& e) {
- // Bad or unexpected data. Give reconnect a shot, it could help...
- TRACE_RB(1, "DebuggerClient::run: protocol exception\n");
- usageLogEvent("DebuggerProtocolException", m_commandCanonical);
- reconnect = true;
- } catch (...) {
- TRACE_RB(1, "DebuggerClient::run: unknown exception\n");
- usageLogEvent("UnknownException", m_commandCanonical);
- Logger::Error("Unhandled exception, exiting.");
- }
- // Note: it's silly to try to reconnect when stopping, or if we have a
- // problem while quitting.
- if (reconnect && !m_stopped && (m_commandCanonical != "quit")) {
- usageLogEvent("reconnect attempt", m_commandCanonical);
- if (DebuggerClient::reconnect()) {
- usageLogEvent("reconnect success", m_commandCanonical);
- continue;
- }
- usageLogEvent("reconnect failed", m_commandCanonical);
- Logger::Error("Unable to reconnect to server, exiting.");
- }
- break;
- }
- usageLogEvent("exit");
- // Closing all proxy connections will force the local proxy to pop out of
- // it's wait, and eventually exit the main thread.
- closeAllConnections();
- hphp_context_exit();
- hphp_session_exit();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // auto-complete
- void DebuggerClient::updateLiveLists() {
- TRACE(2, "DebuggerClient::updateLiveLists\n");
- ReadlineWaitCursor waitCursor;
- CmdInfo::UpdateLiveLists(*this);
- m_acLiveListsDirty = false;
- }
- void DebuggerClient::promptFunctionPrototype() {
- TRACE(2, "DebuggerClient::promptFunctionPrototype\n");
- if (m_acProtoTypePrompted) return;
- m_acProtoTypePrompted = true;
- const char *p0 = rl_line_buffer;
- int len = strlen(p0);
- if (len < 2) return;
- const char *pLast = p0 + len - 1;
- while (pLast > p0 && isspace(*pLast)) --pLast;
- if (pLast == p0 || *pLast-- != '(') return;
- while (pLast > p0 && isspace(*pLast)) --pLast;
- const char *p = pLast;
- while (p >= p0 && (isalnum(*p) || *p == '_')) --p;
- if (p == pLast) return;
- std::string cls;
- std::string func(p + 1, pLast - p);
- if (p > p0 && *p-- == ':' && *p-- == ':') {
- pLast = p;
- while (p >= p0 && (isalnum(*p) || *p == '_')) --p;
- if (pLast > p) {
- cls = std::string(p + 1, pLast - p);
- }
- }
- String output = highlight_code(CmdInfo::GetProtoType(*this, cls, func));
- print("\n%s", output.data());
- rl_forced_update_display();
- }
- bool DebuggerClient::setCompletion(const char* text, int /*start*/,
- int /*end*/) {
- TRACE(2, "DebuggerClient::setCompletion\n");
- if (m_inputState == TakingCommand) {
- parseCommand(rl_line_buffer);
- if (*text) {
- if (!m_args.empty()) {
- m_args.resize(m_args.size() - 1);
- } else {
- m_command.clear();
- }
- }
- }
- return true;
- }
- void DebuggerClient::addCompletion(AutoComplete type) {
- TRACE(2, "DebuggerClient::addCompletion(AutoComplete type)\n");
- if (type < 0 || type >= AutoCompleteCount) {
- Logger::Error("Invalid auto completion enum: %d", type);
- return;
- }
- if (type == AutoCompleteCode) {
- addCompletion(AutoCompleteVariables);
- addCompletion(AutoCompleteConstants);
- addCompletion(AutoCompleteClasses);
- addCompletion(AutoCompleteFunctions);
- addCompletion(AutoCompleteClassMethods);
- addCompletion(AutoCompleteClassProperties);
- addCompletion(AutoCompleteClassConstants);
- addCompletion(AutoCompleteKeyword);
- } else if (type == AutoCompleteKeyword) {
- addCompletion(PHP_KEYWORDS);
- } else {
- m_acLists.push_back((const char **)type);
- }
- if (type == AutoCompleteFunctions || type == AutoCompleteClassMethods) {
- promptFunctionPrototype();
- }
- }
- void DebuggerClient::addCompletion(const char** list) {
- TRACE(2, "DebuggerClient::addCompletion(const char **list)\n");
- m_acLists.push_back(list);
- }
- void DebuggerClient::addCompletion(const char* name) {
- TRACE(2, "DebuggerClient::addCompletion(const char *name)\n");
- m_acStrings.push_back(name);
- }
- void DebuggerClient::addCompletion(const std::vector<std::string>& items) {
- TRACE(2, "DebuggerClient::addCompletion(const std::vector<std::string>)\n");
- m_acItems.insert(m_acItems.end(), items.begin(), items.end());
- }
- char* DebuggerClient::getCompletion(const std::vector<std::string>& items,
- const char* text) {
- TRACE(2, "DebuggerClient::getCompletion(const std::vector<std::string>\n");
- while (++m_acPos < (int)items.size()) {
- auto const p = items[m_acPos].c_str();
- if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
- return strdup(p);
- }
- }
- m_acPos = -1;
- return nullptr;
- }
- std::vector<std::string> DebuggerClient::getAllCompletions(
- const std::string& text
- ) {
- TRACE(2, "DebuggerClient::getAllCompletions\n");
- std::vector<std::string> res;
- if (m_acLiveListsDirty) {
- updateLiveLists();
- }
- for (int i = 0; i < AutoCompleteCount; ++i) {
- auto const& items = m_acLiveLists->get(i);
- for (size_t j = 0; j < items.size(); ++j) {
- auto const p = items[j].c_str();
- if (strncasecmp(p, text.c_str(), text.length()) == 0) {
- res.push_back(std::string(p));
- }
- }
- }
- return res;
- }
- char* DebuggerClient::getCompletion(const std::vector<const char*>& items,
- const char* text) {
- TRACE(2, "DebuggerClient::getCompletion(const std::vector<const char *>\n");
- while (++m_acPos < (int)items.size()) {
- auto const p = items[m_acPos];
- if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
- return strdup(p);
- }
- }
- m_acPos = -1;
- return nullptr;
- }
- static char first_non_whitespace(const char* s) {
- TRACE(2, "DebuggerClient::first_non_whitespace\n");
- while (*s && isspace(*s)) s++;
- return *s;
- }
- char* DebuggerClient::getCompletion(const char* text, int state) {
- TRACE(2, "DebuggerClient::getCompletion\n");
- if (state == 0) {
- m_acLen = strlen(text);
- m_acIndex = 0;
- m_acPos = -1;
- m_acLists.clear();
- m_acStrings.clear();
- m_acItems.clear();
- m_acProtoTypePrompted = false;
- if (m_inputState == TakingCommand) {
- switch (first_non_whitespace(rl_line_buffer)) {
- case '<':
- if (strncasecmp(m_command.substr(0, 5).c_str(), "<?php", 5)) {
- addCompletion("<?php");
- break;
- }
- case '@':
- case '=':
- case '$': {
- addCompletion(AutoCompleteCode);
- break;
- }
- default: {
- if (m_command.empty()) {
- addCompletion(GetCommands());
- addCompletion("@");
- addCompletion("=");
- addCompletion("<?php");
- addCompletion("?>");
- } else {
- auto cmd = createCommand();
- if (cmd) {
- if (cmd->is(DebuggerCommand::KindOfRun)) playMacro("startup");
- cmd->list(*this);
- }
- }
- break;
- }
- }
- } else {
- assertx(m_inputState == TakingCode);
- if (!*rl_line_buffer) {
- addCompletion("?>"); // so we tab, we're done
- } else {
- addCompletion(AutoCompleteCode);
- }
- }
- }
- for (; m_acIndex < (int)m_acLists.size(); m_acIndex++) {
- const char **list = m_acLists[m_acIndex];
- if ((int64_t)list == AutoCompleteFileNames) {
- char *p = rl_filename_completion_function(text, ++m_acPos);
- if (p) return p;
- } else if ((int64_t)list >= 0 && (int64_t)list < AutoCompleteCount) {
- if (m_acLiveListsDirty) {
- updateLiveLists();
- assertx(!m_acLiveListsDirty);
- }
- char *p = getCompletion(m_acLiveLists->get(int64_t(list)), text);
- if (p) return p;
- } else {
- for (const char *p = list[++m_acPos]; p; p = list[++m_acPos]) {
- if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
- return strdup(p);
- }
- }
- }
- m_acPos = -1;
- }
- char *p = getCompletion(m_acStrings, text);
- if (p) return p;
- return getCompletion(m_acItems, text);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // main
- // Execute the initial connection protocol with a machine. A connection has been
- // established, and the proxy has responded with an interrupt giving us initial
- // control. Send breakpoints to the server, and then attach to the sandbox
- // if necessary. If we attach to a sandbox, then the process is off and running
- // again (CmdMachine continues execution on a successful attach) so return false
- // to indicate that a client should wait for another interrupt before attempting
- // further communication. Returns true if the protocol is complete and the
- // machine is at an interrupt.
- bool DebuggerClient::initializeMachine() {
- TRACE(2, "DebuggerClient::initializeMachine\n");
- // set/clear intercept for RPC thread
- if (!m_machines.empty() && m_machine == m_machines[0]) {
- CmdMachine::UpdateIntercept(*this, m_machine->m_rpcHost,
- m_machine->m_rpcPort);
- }
- // upload breakpoints
- if (!m_breakpoints.empty()) {
- info("Updating breakpoints...");
- CmdBreak::SendClientBreakpointListToServer(*this);
- }
- // attaching to default sandbox
- int waitForSandbox = false;
- if (!m_machine->m_sandboxAttached) {
- const char *user = m_options.user.empty() ?
- nullptr : m_options.user.c_str();
- m_machine->m_sandboxAttached = (waitForSandbox =
- CmdMachine::AttachSandbox(*this, user, m_options.sandbox.c_str()));
- if (!m_machine->m_sandboxAttached) {
- Logger::Error("Unable to communicate with default sandbox.");
- }
- }
- m_machine->m_initialized = true;
- if (waitForSandbox) {
- // Return false to wait for next interrupt from server
- return false;
- }
- return true;
- }
- // The main execution loop of DebuggerClient. This waits for interrupts from
- // the server (and responds to polls for signals). On interrupt, it presents a
- // command prompt, and continues pumping interrupts when a command lets the
- // machine run again. For nested loops it returns the command that completed
- // the loop, which will match the expectedCmd passed in. For all loop types,
- // throws one of a variety of exceptions for various errors, and throws
- // DebuggerClientExitException when the event loop is terminated due to the
- // client stopping.
- DebuggerCommandPtr DebuggerClient::eventLoop(EventLoopKind loopKind,
- int expectedCmd,
- const char *caller) {
- TRACE(2, "DebuggerClient::eventLoop\n");
- ARRPROV_USE_RUNTIME_LOCATION();
- if (loopKind == NestedWithExecution) {
- // Some callers have caused the server to start executing more PHP, so
- // update the machine/client state accordingly.
- m_inputState = TakingInterrupt;
- m_machine->m_interrupting = false;
- }
- while (!m_stopped) {
- DebuggerCommandPtr cmd;
- if (DebuggerCommand::Receive(m_machine->m_thrift, cmd, caller)) {
- if (!cmd) {
- Logger::Error("Unable to communicate with server. Server's down?");
- throw DebuggerServerLostException();
- }
- if (cmd->is(DebuggerCommand::KindOfSignal) ||
- cmd->is(DebuggerCommand::KindOfAuth)) {
- // Respond to polling from the server.
- cmd->onClient(*this);
- continue;
- }
- if (!cmd->getWireError().empty()) {
- error("wire error: %s", cmd->getWireError().data());
- }
- if ((loopKind != TopLevel) &&
- cmd->is((DebuggerCommand::Type)expectedCmd)) {
- // For the nested cases, the caller has sent a cmd to the server and is
- // expecting a specific response. When we get it, return it.
- usageLogEvent("command done", folly::to<std::string>(expectedCmd));
- m_machine->m_interrupting = true; // Machine is stopped
- m_inputState = TakingCommand;
- return cmd;
- }
- if ((loopKind == Nested) || !cmd->is(DebuggerCommand::KindOfInterrupt)) {
- Logger::Error("Received bad cmd type %d, unable to communicate "
- "with server.", cmd->getType());
- throw DebuggerProtocolException();
- }
- m_sigCount = 0;
- auto intr = std::dynamic_pointer_cast<CmdInterrupt>(cmd);
- Debugger::UsageLogInterrupt("terminal", getSandboxId(), *intr.get());
- cmd->onClient(*this);
- // When we make a new connection to a machine, we have to wait for it
- // to interrupt us before we can send it any messages. This is our
- // opportunity to complete the connection and make it ready to use.
- if (!m_machine->m_initialized) {
- if (!initializeMachine()) {
- // False means the machine is running and we need to wait for
- // another interrupt.
- continue;
- }
- }
- // Execution has been interrupted, so go ahead and give the user
- // the prompt back.
- m_machine->m_interrupting = true; // Machine is stopped
- m_inputState = TakingCommand;
- console(); // Prompt loop
- m_inputState = TakingInterrupt;
- m_machine->m_interrupting = false; // Machine is running again.
- if (m_scriptMode) {
- print("Waiting for server response");
- }
- }
- }
- throw DebuggerClientExitException(); // Stopped, so exit.
- }
- // Execute the interactive command loop for the debugger client. This will
- // present the prompt, wait for user input, and execute commands, then rinse
- // and repeat. The loop terminates when a command is executed that causes the
- // machine to resume execution, or which should cause the client to exit.
- // This function is only entered when the machine being debugged is paused.
- //
- // If this function returns it means the process is running again.
- // NB: exceptions derived from DebuggerException or DebuggerClientExeption
- // indicate the machine remains paused.
- void DebuggerClient::console() {
- TRACE(2, "DebuggerClient::console\n");
- while (true) {
- const char *line = nullptr;
- std::string holder;
- if (m_macroPlaying) {
- if (m_macroPlaying->m_index < m_macroPlaying->m_cmds.size()) {
- holder = m_macroPlaying->m_cmds[m_macroPlaying->m_index++];
- line = holder.c_str();
- } else {
- m_macroPlaying.reset();
- }
- }
- if (line == nullptr) {
- line = readline(getPrompt().c_str());
- if (line == nullptr) {
- // treat ^D as quit
- print("quit");
- line = "quit";
- } else {
- #ifdef USE_EDITLINE
- print("%s", line); // Stay consistent with the readline library
- #endif
- }
- } else if (!NoPrompt && RuntimeOption::EnableDebuggerPrompt) {
- print("%s%s", getPrompt().c_str(), line);
- }
- if (*line && !m_macroPlaying &&
- strcasecmp(line, "QUIT") != 0 &&
- strcasecmp(line, "QUI") != 0 &&
- strcasecmp(line, "QU") != 0 &&
- strcasecmp(line, "Q") != 0) {
- // even if line is bad command, we still want to remember it, so
- // people can go back and fix typos
- HIST_ENTRY *last_entry = nullptr;
- if (history_length > 0 &&
- (last_entry = history_get(history_length + history_base - 1))) {
- // Make sure we aren't duplicating history entries
- if (strcmp(line, last_entry->line)) {
- add_history(line);
- }
- } else {
- // Add history regardless, since we know that there are no
- // duplicate entries.
- add_history(line);
- }
- }
- AdjustScreenMetrics();
- if (*line) {
- if (parse(line)) {
- try {
- record(line);
- m_prevCmd = m_command;
- if (!process()) {
- error("command \"" + m_command + "\" not found");
- m_command.clear();
- }
- } catch (DebuggerConsoleExitException& e) {
- return;
- }
- }
- } else if (m_inputState == TakingCommand) {
- switch (m_prevCmd[0]) {
- case 'l': // list
- m_args.clear(); // as if just "list"
- // fall through
- case 'c': // continue
- case 's': // step
- case 'n': // next
- case 'o': // out
- try {
- record(line);
- m_command = m_prevCmd;
- process(); // replay the same command
- } catch (DebuggerConsoleExitException& e) {
- return;
- }
- break;
- }
- }
- }
- not_reached();
- }
- const StaticString
- s_file("file"),
- s_line("line");
- //
- // Called when a breakpoint is reached, to produce the console
- // spew showing the code around the breakpoint.
- //
- void DebuggerClient::shortCode(BreakPointInfoPtr bp) {
- TRACE(2, "DebuggerClient::shortCode\n");
- if (bp && !bp->m_file.empty() && bp->m_line1) {
- Variant source = CmdList::GetSourceFile(*this, bp->m_file);
- if (source.isString()) {
- // Line and column where highlight should start and end
- int beginHighlightLine = bp->m_line1;
- int beginHighlightColumn = bp->m_char1;
- int endHighlightLine = bp->m_line2;
- int endHighlightColumn = bp->m_char2;
- // Lines where source listing should start and end
- int firstLine = std::max(beginHighlightLine - 1, 1);
- int lastLine = endHighlightLine + 1;
- int maxLines = getDebuggerClientMaxCodeLines();
- // If MaxCodeLines == 0: don't spew any code after a [s]tep or [n]ext
- // command.
- if (maxLines == 0) {
- return;
- }
- // If MaxCodeLines > 0: limit spew to a maximum of # lines.
- if (maxLines > 0) {
- int numHighlightLines = endHighlightLine - beginHighlightLine + 1;
- if (numHighlightLines > maxLines) {
- // If there are too many highlight lines, truncate spew
- // by setting lastLine ...
- lastLine = beginHighlightLine + maxLines - 1;
- // ... and set endHighlightLine/Column so that it is just past the end
- // of the spew, and all code up to the truncation will be highlighted.
- endHighlightLine = lastLine + 1;
- endHighlightColumn = 1;
- }
- }
- code(source.toString(), firstLine, lastLine,
- beginHighlightLine,
- beginHighlightColumn,
- endHighlightLine,
- endHighlightColumn);
- }
- }
- }
- bool DebuggerClient::code(const String& source, int line1 /*= 0*/,
- int line2 /*= 0*/,
- int lineFocus0 /* = 0 */, int charFocus0 /* = 0 */,
- int lineFocus1 /* = 0 */, int charFocus1 /* = 0 */) {
- TRACE(2, "DebuggerClient::code\n");
- if (line1 == 0 && line2 == 0) {
- String highlighted = highlight_code(source, 0, lineFocus0, charFocus0,
- lineFocus1, charFocus1);
- if (!highlighted.empty()) {
- print(highlighted);
- return true;
- }
- return false;
- }
- String highlighted = highlight_php(source, 1, lineFocus0, charFocus0,
- lineFocus1, charFocus1);
- int line = 1;
- const char *begin = highlighted.data();
- StringBuffer sb;
- for (const char *p = begin; *p; p++) {
- if (*p == '\n') {
- if (line >= line1) {
- sb.append(begin, p - begin + 1);
- }
- if (++line > line2) break;
- begin = p + 1;
- }
- }
- if (!sb.empty()) {
- print("%s%s", sb.data(),
- UseColor && RuntimeOption::EnableDebuggerColor ? ANSI_COLOR_END : "\0");
- return true;
- }
- return false;
- }
- char DebuggerClient::ask(const char *fmt, ...) {
- TRACE(2, "DebuggerClient::ask\n");
- std::string msg;
- va_list ap;
- va_start(ap, fmt);
- string_vsnprintf(msg, fmt, ap); va_end(ap);
- if (UseColor && InfoColor && RuntimeOption::EnableDebuggerColor) {
- msg = InfoColor + msg + ANSI_COLOR_END;
- }
- fwrite(msg.data(), 1, msg.length(), stdout);
- fflush(stdout);
- auto input = readline("");
- if (input == nullptr) return ' ';
- #ifdef USE_EDITLINE
- print("%s", input); // Stay consistent with the readline library
- #endif
- if (strlen(input) > 0) return tolower(input[0]);
- return ' ';
- }
- #define DWRITE(ptr, size, nmemb, stream) \
- do { \
- /* LogFile debugger setting */ \
- FILE *f = getLogFileHandler(); \
- if (f != nullptr) { \
- fwrite(ptr, size, nmemb, f); \
- } \
- \
- /* For debugging, still output to stdout */ \
- fwrite(ptr, size, nmemb, stream); \
- } while (0) \
- void DebuggerClient::print(const char* fmt, ...) {
- TRACE(2, "DebuggerClient::print(const char* fmt, ...)\n");
- std::string msg;
- va_list ap;
- va_start(ap, fmt);
- string_vsnprintf(msg, fmt, ap); va_end(ap);
- print(msg);
- }
- void DebuggerClient::print(const String& msg) {
- TRACE(2, "DebuggerClient::print(const String& msg)\n");
- DWRITE(msg.data(), 1, msg.length(), stdout);
- DWRITE("\n", 1, 1, stdout);
- fflush(stdout);
- }
- void DebuggerClient::print(const std::string& msg) {
- TRACE(2, "DebuggerClient::print(const std::string& msg)\n");
- DWRITE(msg.data(), 1, msg.size(), stdout);
- DWRITE("\n", 1, 1, stdout);
- fflush(stdout);
- }
- void DebuggerClient::print(folly::StringPiece msg) {
- TRACE(2, "DebuggerClient::print(folly::StringPiece msg)\n");
- DWRITE(msg.data(), 1, msg.size(), stdout);
- DWRITE("\n", 1, 1, stdout);
- fflush(stdout);
- }
- #define IMPLEMENT_COLOR_OUTPUT(name, where, color) \
- void DebuggerClient::name(folly::StringPiece msg) { \
- if (UseColor && color && RuntimeOption::EnableDebuggerColor) { \
- DWRITE(color, 1, strlen(color), where); \
- } \
- DWRITE(msg.data(), 1, msg.size(), where); \
- if (UseColor && color && RuntimeOption::EnableDebuggerColor) { \
- DWRITE(ANSI_COLOR_END, 1, strlen(ANSI_COLOR_END), where); \
- } \
- DWRITE("\n", 1, 1, where); \
- fflush(where); \
- } \
- \
- void DebuggerClient::name(const String& msg) { \
- name(msg.slice()); \
- } \
- \
- void DebuggerClient::name(const std::string& msg) { \
- name(folly::StringPiece{msg}); \
- } \
- \
- void DebuggerClient::name(const char *fmt, ...) { \
- std::string msg; \
- va_list ap; \
- va_start(ap, fmt); \
- string_vsnprintf(msg, fmt, ap); va_end(ap); \
- name(msg); \
- } \
- IMPLEMENT_COLOR_OUTPUT(help, stdout, HelpColor);
- IMPLEMENT_COLOR_OUTPUT(info, stdout, InfoColor);
- IMPLEMENT_COLOR_OUTPUT(output, stdout, OutputColor);
- IMPLEMENT_COLOR_OUTPUT(error, stderr, ErrorColor);
- #undef DWRITE
- #undef IMPLEMENT_COLOR_OUTPUT
- std::string DebuggerClient::wrap(const std::string &s) {
- TRACE(2, "DebuggerClient::wrap\n");
- String ret = wordwrap(String(s.c_str(), s.size(), CopyString), LineWidth - 4,
- "\n", true);
- return std::string(ret.data(), ret.size());
- }
- void DebuggerClient::helpTitle(const char *title) {
- TRACE(2, "DebuggerClient::helpTitle\n");
- help(FormatTitle(title));
- }
- void DebuggerClient::helpCmds(const char *cmd, const char *desc, ...) {
- TRACE(2, "DebuggerClient::helpCmds(const char *cmd, const char *desc,...)\n");
- std::vector<const char *> cmds;
- cmds.push_back(cmd);
- cmds.push_back(desc);
- va_list ap;
- va_start(ap…
Large files files are truncated, but you can click here to view the full file