PageRenderTime 64ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/hphp/runtime/debugger/debugger_client.cpp

http://github.com/facebook/hiphop-php
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

  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/debugger/debugger_client.h"
  17. #include <signal.h>
  18. #include <fstream>
  19. #include "hphp/runtime/base/array-init.h"
  20. #include "hphp/runtime/base/array-iterator.h"
  21. #include "hphp/runtime/base/builtin-functions.h"
  22. #include "hphp/runtime/base/config.h"
  23. #include "hphp/runtime/base/preg.h"
  24. #include "hphp/runtime/base/program-functions.h"
  25. #include "hphp/runtime/base/string-util.h"
  26. #include "hphp/runtime/base/variable-serializer.h"
  27. #include "hphp/runtime/debugger/cmd/all.h"
  28. #include "hphp/runtime/debugger/debugger_command.h"
  29. #include "hphp/runtime/ext/sockets/ext_sockets.h"
  30. #include "hphp/runtime/ext/std/ext_std_network.h"
  31. #include "hphp/runtime/ext/string/ext_string.h"
  32. #include "hphp/runtime/vm/treadmill.h"
  33. #include "hphp/util/logger.h"
  34. #include "hphp/util/process.h"
  35. #include "hphp/util/stack-trace.h"
  36. #include "hphp/util/string-vsnprintf.h"
  37. #include "hphp/util/text-art.h"
  38. #include "hphp/util/text-color.h"
  39. #include <boost/scoped_ptr.hpp>
  40. #include <folly/Conv.h>
  41. #include <folly/portability/Unistd.h>
  42. #define USE_VARARGS
  43. #define PREFER_STDARG
  44. #ifdef USE_EDITLINE
  45. #include <editline/readline.h>
  46. #include <histedit.h>
  47. #else
  48. #include <readline/readline.h>
  49. #include <readline/history.h>
  50. #include <algorithm>
  51. #include <map>
  52. #include <memory>
  53. #include <set>
  54. #include <vector>
  55. #endif
  56. using namespace HPHP::TextArt;
  57. #define PHP_WORD_BREAK_CHARACTERS " \t\n\"\\'`@=;,|{[()]}+*%^!~&"
  58. namespace HPHP { namespace Eval {
  59. ///////////////////////////////////////////////////////////////////////////////
  60. TRACE_SET_MOD(debugger);
  61. static boost::scoped_ptr<DebuggerClient> debugger_client;
  62. const StaticString
  63. s_name("name"),
  64. s_cmds("cmds"),
  65. s_hhvm_never_save_config("hhvm.never_save_config");
  66. static String wordwrap(const String& str, int width /* = 75 */,
  67. const String& wordbreak /* = "\n" */,
  68. bool cut /* = false */) {
  69. Array args = Array::Create();
  70. args.append(str);
  71. args.append(width);
  72. args.append(wordbreak);
  73. args.append(cut);
  74. return vm_call_user_func("wordwrap", args).toString();
  75. }
  76. struct DebuggerExtension final : Extension {
  77. DebuggerExtension() : Extension("hhvm.debugger", NO_EXTENSION_VERSION_YET) {}
  78. } s_debugger_extension;
  79. static DebuggerClient& getStaticDebuggerClient() {
  80. TRACE(2, "DebuggerClient::getStaticDebuggerClient\n");
  81. /*
  82. * DebuggerClient acquires global mutexes in its constructor, so we
  83. * allocate debugger_client lazily to ensure that all of the
  84. * global mutexes have been initialized before we enter the
  85. * constructor.
  86. *
  87. * This initialization is thread-safe because program-functions.cpp
  88. * must call Debugger::StartClient (which ends up here) before any
  89. * additional threads are created.
  90. */
  91. if (!debugger_client) {
  92. debugger_client.reset(new DebuggerClient);
  93. }
  94. return *debugger_client;
  95. }
  96. ///////////////////////////////////////////////////////////////////////////////
  97. // readline setups
  98. static char* debugger_generator(const char* text, int state) {
  99. TRACE(2, "DebuggerClient::debugger_generator\n");
  100. return getStaticDebuggerClient().getCompletion(text, state);
  101. }
  102. static char **debugger_completion(const char *text, int start, int end) {
  103. TRACE(2, "DebuggerClient::debugger_completion\n");
  104. if (getStaticDebuggerClient().setCompletion(text, start, end)) {
  105. return rl_completion_matches((char*)text, &debugger_generator);
  106. }
  107. return nullptr;
  108. }
  109. #ifndef USE_EDITLINE
  110. static rl_hook_func_t *old_rl_startup_hook = nullptr;
  111. static int saved_history_line_to_use = -1;
  112. static int last_saved_history_line = -1;
  113. static bool history_full() {
  114. return (history_is_stifled() && history_length >= history_max_entries);
  115. }
  116. static int set_saved_history() {
  117. if (history_full() && saved_history_line_to_use < history_length - 1) {
  118. saved_history_line_to_use++;
  119. }
  120. if (saved_history_line_to_use >= 0) {
  121. rl_get_previous_history(history_length - saved_history_line_to_use, 0);
  122. last_saved_history_line = saved_history_line_to_use;
  123. }
  124. saved_history_line_to_use = -1;
  125. rl_startup_hook = old_rl_startup_hook;
  126. return 0;
  127. }
  128. static int operate_and_get_next(int /*count*/, int c) {
  129. /* Accept the current line. */
  130. rl_newline (1, c);
  131. /* Find the current line, and find the next line to use. */
  132. int where = where_history();
  133. if (history_full() || (where >= history_length - 1)) {
  134. saved_history_line_to_use = where;
  135. } else {
  136. saved_history_line_to_use = where + 1;
  137. }
  138. old_rl_startup_hook = rl_startup_hook;
  139. rl_startup_hook = set_saved_history;
  140. return 0;
  141. }
  142. #endif
  143. static void debugger_signal_handler(int sig) {
  144. TRACE(2, "DebuggerClient::debugger_signal_handler\n");
  145. getStaticDebuggerClient().onSignal(sig);
  146. }
  147. void DebuggerClient::onSignal(int /*sig*/) {
  148. TRACE(2, "DebuggerClient::onSignal\n");
  149. if (m_inputState == TakingInterrupt) {
  150. if (m_sigCount == 0) {
  151. usageLogEvent("signal start");
  152. info("Pausing program execution, please wait...");
  153. } else if (m_sigCount == 1) {
  154. usageLogEvent("signal wait");
  155. help("Still attempting to pause program execution...");
  156. help(" Sometimes this takes a few seconds, so give it a chance,");
  157. help(" or press ctrl-c again to give up and terminate the debugger.");
  158. } else {
  159. usageLogEvent("signal quit");
  160. error("Debugger is quitting.");
  161. if (!getStaticDebuggerClient().isLocal()) {
  162. error(" Note: the program may still be running on the server.");
  163. }
  164. quit(); // NB: the machine is running, so can't send a real CmdQuit.
  165. return;
  166. }
  167. m_sigCount++;
  168. m_sigNum = CmdSignal::SignalBreak;
  169. } else {
  170. rl_line_buffer[0] = '\0';
  171. #ifndef USE_EDITLINE
  172. rl_free_line_state();
  173. rl_cleanup_after_signal();
  174. #endif
  175. rl_redisplay();
  176. }
  177. }
  178. int DebuggerClient::pollSignal() {
  179. TRACE(2, "DebuggerClient::pollSignal\n");
  180. if (m_scriptMode) {
  181. print(".....Debugger client still waiting for server response.....");
  182. }
  183. int ret = m_sigNum;
  184. m_sigNum = CmdSignal::SignalNone;
  185. return ret;
  186. }
  187. ///////////////////////////////////////////////////////////////////////////////
  188. /**
  189. * Initialization and shutdown.
  190. */
  191. struct ReadlineApp {
  192. ReadlineApp() {
  193. TRACE(2, "ReadlineApp::ReadlineApp\n");
  194. DebuggerClient::AdjustScreenMetrics();
  195. rl_attempted_completion_function = debugger_completion;
  196. rl_basic_word_break_characters = PHP_WORD_BREAK_CHARACTERS;
  197. #ifndef USE_EDITLINE
  198. rl_bind_keyseq("\\C-o", operate_and_get_next);
  199. rl_catch_signals = 0;
  200. #endif
  201. signal(SIGINT, debugger_signal_handler);
  202. TRACE(3, "ReadlineApp::ReadlineApp, about to call read_history\n");
  203. read_history((Process::GetHomeDirectory() +
  204. DebuggerClient::HistoryFileName).c_str());
  205. TRACE(3, "ReadlineApp::ReadlineApp, done calling read_history\n");
  206. }
  207. ~ReadlineApp() {
  208. TRACE(2, "ReadlineApp::~ReadlineApp\n");
  209. write_history((Process::GetHomeDirectory() +
  210. DebuggerClient::HistoryFileName).c_str());
  211. }
  212. };
  213. /**
  214. * Displaying a spinning wait icon.
  215. */
  216. struct ReadlineWaitCursor {
  217. ReadlineWaitCursor()
  218. : m_thread(this, &ReadlineWaitCursor::animate), m_waiting(true) {
  219. TRACE(2, "ReadlineWaitCursor::ReadlineWaitCursor\n");
  220. m_thread.start();
  221. }
  222. ~ReadlineWaitCursor() {
  223. TRACE(2, "ReadlineWaitCursor::~ReadlineWaitCursor\n");
  224. m_waiting = false;
  225. m_thread.waitForEnd();
  226. }
  227. void animate() {
  228. if (rl_point <= 0) return;
  229. auto p = rl_point - 1;
  230. auto orig = rl_line_buffer[p];
  231. while (m_waiting) {
  232. frame('|', p); frame('/', p); frame('-', p); frame('\\', p);
  233. rl_line_buffer[p] = orig;
  234. rl_redisplay();
  235. }
  236. }
  237. private:
  238. AsyncFunc<ReadlineWaitCursor> m_thread;
  239. bool m_waiting;
  240. void frame(char ch, int point) {
  241. rl_line_buffer[point] = ch;
  242. rl_redisplay();
  243. usleep(100000);
  244. }
  245. };
  246. ///////////////////////////////////////////////////////////////////////////////
  247. int DebuggerClient::LineWidth = 76;
  248. int DebuggerClient::CodeBlockSize = 20;
  249. int DebuggerClient::ScrollBlockSize = 20;
  250. const char *DebuggerClient::LineNoFormat = "%4d ";
  251. const char *DebuggerClient::LineNoFormatWithStar = "%4d*";
  252. const char *DebuggerClient::LocalPrompt = "hphpd";
  253. const char *DebuggerClient::ConfigFileName = ".hphpd.ini";
  254. const char *DebuggerClient::LegacyConfigFileName = ".hphpd.hdf";
  255. const char *DebuggerClient::HistoryFileName = ".hphpd.history";
  256. std::string DebuggerClient::HomePrefix = "/home";
  257. bool DebuggerClient::UseColor = true;
  258. bool DebuggerClient::NoPrompt = false;
  259. const char *DebuggerClient::HelpColor = nullptr;
  260. const char *DebuggerClient::InfoColor = nullptr;
  261. const char *DebuggerClient::OutputColor = nullptr;
  262. const char *DebuggerClient::ErrorColor = nullptr;
  263. const char *DebuggerClient::ItemNameColor = nullptr;
  264. const char *DebuggerClient::HighlightForeColor = nullptr;
  265. const char *DebuggerClient::HighlightBgColor = nullptr;
  266. const char *DebuggerClient::DefaultCodeColors[] = {
  267. /* None */ nullptr, nullptr,
  268. /* Keyword */ nullptr, nullptr,
  269. /* Comment */ nullptr, nullptr,
  270. /* String */ nullptr, nullptr,
  271. /* Variable */ nullptr, nullptr,
  272. /* Html */ nullptr, nullptr,
  273. /* Tag */ nullptr, nullptr,
  274. /* Declaration */ nullptr, nullptr,
  275. /* Constant */ nullptr, nullptr,
  276. /* LineNo */ nullptr, nullptr,
  277. };
  278. void DebuggerClient::LoadColors(const IniSetting::Map& ini, Hdf hdf) {
  279. TRACE(2, "DebuggerClient::LoadColors\n");
  280. HelpColor = LoadColor(ini, hdf, "Color.Help", "BROWN");
  281. InfoColor = LoadColor(ini, hdf, "Color.Info", "GREEN");
  282. OutputColor = LoadColor(ini, hdf, "Color.Output", "CYAN");
  283. ErrorColor = LoadColor(ini, hdf, "Color.Error", "RED");
  284. ItemNameColor = LoadColor(ini, hdf, "Color.ItemName", "GRAY");
  285. HighlightForeColor = LoadColor(ini, hdf, "Color.HighlightForeground", "RED");
  286. HighlightBgColor = LoadBgColor(ini, hdf, "Color.HighlightBackground", "GRAY");
  287. Hdf code = hdf["Code"];
  288. LoadCodeColor(CodeColorKeyword, ini, hdf, "Color.Code.Keyword",
  289. "CYAN");
  290. LoadCodeColor(CodeColorComment, ini, hdf, "Color.Code.Comment",
  291. "RED");
  292. LoadCodeColor(CodeColorString, ini, hdf, "Color.Code.String",
  293. "GREEN");
  294. LoadCodeColor(CodeColorVariable, ini, hdf, "Color.Code.Variable",
  295. "BROWN");
  296. LoadCodeColor(CodeColorHtml, ini, hdf, "Color.Code.Html",
  297. "GRAY");
  298. LoadCodeColor(CodeColorTag, ini, hdf, "Color.Code.Tag",
  299. "MAGENTA");
  300. LoadCodeColor(CodeColorDeclaration, ini, hdf, "Color.Code.Declaration",
  301. "BLUE");
  302. LoadCodeColor(CodeColorConstant, ini, hdf, "Color.Code.Constant",
  303. "MAGENTA");
  304. LoadCodeColor(CodeColorLineNo, ini, hdf, "Color.Code.LineNo",
  305. "GRAY");
  306. }
  307. const char *DebuggerClient::LoadColor(const IniSetting::Map& ini, Hdf hdf,
  308. const std::string& setting,
  309. const char *defaultName) {
  310. TRACE(2, "DebuggerClient::LoadColor\n");
  311. const char *name = Config::Get(ini, hdf, setting, defaultName);
  312. hdf = name; // for starter
  313. const char *color = get_color_by_name(name);
  314. if (color == nullptr) {
  315. Logger::Error("Bad color name %s", name);
  316. color = get_color_by_name(defaultName);
  317. }
  318. return color;
  319. }
  320. const char *DebuggerClient::LoadBgColor(const IniSetting::Map& ini, Hdf hdf,
  321. const std::string& setting,
  322. const char *defaultName) {
  323. TRACE(2, "DebuggerClient::LoadBgColor\n");
  324. const char *name = Config::Get(ini, hdf, setting, defaultName);
  325. hdf = name; // for starter
  326. const char *color = get_bgcolor_by_name(name);
  327. if (color == nullptr) {
  328. Logger::Error("Bad color name %s", name);
  329. color = get_bgcolor_by_name(defaultName);
  330. }
  331. return color;
  332. }
  333. void DebuggerClient::LoadCodeColor(CodeColor index, const IniSetting::Map& ini,
  334. Hdf hdf, const std::string& setting,
  335. const char *defaultName) {
  336. TRACE(2, "DebuggerClient::LoadCodeColor\n");
  337. const char *color = LoadColor(ini, hdf, setting, defaultName);
  338. DefaultCodeColors[index * 2] = color;
  339. DefaultCodeColors[index * 2 + 1] = color ? ANSI_COLOR_END : nullptr;
  340. }
  341. req::ptr<Socket> DebuggerClient::Start(const DebuggerClientOptions &options) {
  342. TRACE(2, "DebuggerClient::Start\n");
  343. auto ret = getStaticDebuggerClient().connectLocal();
  344. getStaticDebuggerClient().start(options);
  345. return ret;
  346. }
  347. void DebuggerClient::Stop() {
  348. TRACE(2, "DebuggerClient::Stop\n");
  349. if (debugger_client) {
  350. debugger_client.reset();
  351. }
  352. }
  353. void DebuggerClient::AdjustScreenMetrics() {
  354. TRACE(2, "entered: DebuggerClient::AdjustScreenMetrics\n");
  355. int rows = 0; int cols = 0;
  356. rl_get_screen_size(&rows, &cols);
  357. if (rows > 0 && cols > 0) {
  358. LineWidth = cols - 4;
  359. ScrollBlockSize = CodeBlockSize = rows - (rows >> 2);
  360. }
  361. TRACE(2, "leaving: DebuggerClient::AdjustScreenMetrics\n");
  362. }
  363. bool DebuggerClient::IsValidNumber(const std::string &arg) {
  364. TRACE(2, "DebuggerClient::IsValidNumber\n");
  365. if (arg.empty()) return false;
  366. for (auto c : arg) {
  367. if (!isdigit(c)) {
  368. return false;
  369. }
  370. }
  371. return true;
  372. }
  373. String DebuggerClient::FormatVariable(
  374. const Variant& v,
  375. char format /* = 'd' */
  376. ) {
  377. TRACE(2, "DebuggerClient::FormatVariable\n");
  378. String value;
  379. try {
  380. auto const t =
  381. format == 'r' ? VariableSerializer::Type::PrintR :
  382. format == 'v' ? VariableSerializer::Type::VarDump :
  383. VariableSerializer::Type::DebuggerDump;
  384. VariableSerializer vs(t, 0, 2);
  385. value = vs.serialize(v, true);
  386. } catch (const StringBufferLimitException& e) {
  387. value = "Serialization limit reached";
  388. } catch (...) {
  389. assertx(false);
  390. throw;
  391. }
  392. return value;
  393. }
  394. /*
  395. * Serializes a Variant, and truncates it to a limit if necessary. Returns the
  396. * truncated result, and the number of bytes truncated.
  397. */
  398. String DebuggerClient::FormatVariableWithLimit(const Variant& v, int maxlen) {
  399. assertx(maxlen >= 0);
  400. VariableSerializer vs(VariableSerializer::Type::DebuggerDump, 0, 2);
  401. auto const value = vs.serializeWithLimit(v, maxlen + 1);
  402. if (value.length() <= maxlen) {
  403. return value;
  404. }
  405. StringBuffer sb;
  406. sb.append(folly::StringPiece{value.data(), static_cast<size_t>(maxlen)});
  407. sb.append(" ...(omitted)");
  408. return sb.detach();
  409. }
  410. String DebuggerClient::FormatInfoVec(const IDebuggable::InfoVec &info,
  411. int *nameLen /* = NULL */) {
  412. TRACE(2, "DebuggerClient::FormatInfoVec\n");
  413. // vertical align names
  414. int maxlen = 0;
  415. for (unsigned int i = 0; i < info.size(); i++) {
  416. int len = strlen(info[i].first);
  417. if (len > maxlen) maxlen = len;
  418. }
  419. // print
  420. StringBuffer sb;
  421. for (unsigned int i = 0; i < info.size(); i++) {
  422. if (ItemNameColor) sb.append(ItemNameColor);
  423. std::string name = info[i].first;
  424. name += ": ";
  425. sb.append(name.substr(0, maxlen + 4));
  426. if (ItemNameColor) sb.append(ANSI_COLOR_END);
  427. if (OutputColor) sb.append(OutputColor);
  428. sb.append(info[i].second);
  429. if (OutputColor) sb.append(ANSI_COLOR_END);
  430. sb.append("\n");
  431. }
  432. if (nameLen) *nameLen = maxlen + 4;
  433. return sb.detach();
  434. }
  435. String DebuggerClient::FormatTitle(const char *title) {
  436. TRACE(2, "DebuggerClient::FormatTitle\n");
  437. String dash = HHVM_FN(str_repeat)(BOX_H, (LineWidth - strlen(title)) / 2 - 4);
  438. StringBuffer sb;
  439. sb.append("\n");
  440. sb.append(" ");
  441. sb.append(dash);
  442. sb.append(" "); sb.append(title); sb.append(" ");
  443. sb.append(dash);
  444. sb.append("\n");
  445. return sb.detach();
  446. }
  447. ///////////////////////////////////////////////////////////////////////////////
  448. DebuggerClient::DebuggerClient()
  449. : m_tutorial(0), m_scriptMode(false),
  450. m_logFile(""), m_logFileHandler(nullptr),
  451. m_mainThread(this, &DebuggerClient::run), m_stopped(false),
  452. m_inputState(TakingCommand),
  453. m_sigNum(CmdSignal::SignalNone), m_sigCount(0),
  454. m_acLen(0), m_acIndex(0), m_acPos(0), m_acLiveListsDirty(true),
  455. m_threadId(0), m_listLine(0), m_listLineFocus(0),
  456. m_frame(0),
  457. m_unknownCmd(false) {
  458. TRACE(2, "DebuggerClient::DebuggerClient\n");
  459. Debugger::InitUsageLogging();
  460. }
  461. DebuggerClient::~DebuggerClient() {
  462. TRACE(2, "DebuggerClient::~DebuggerClient\n");
  463. m_stopped = true;
  464. m_mainThread.waitForEnd();
  465. FILE *f = getLogFileHandler();
  466. if (f != nullptr) {
  467. fclose(f);
  468. setLogFileHandler(nullptr);
  469. }
  470. }
  471. void DebuggerClient::closeAllConnections() {
  472. TRACE(2, "DebuggerClient::closeAllConnections\n");
  473. for (unsigned int i = 0; i < m_machines.size(); i++) {
  474. m_machines[i]->m_thrift.close();
  475. }
  476. }
  477. bool DebuggerClient::isLocal() {
  478. TRACE(2, "DebuggerClient::isLocal\n");
  479. return m_machines[0] == m_machine;
  480. }
  481. bool DebuggerClient::connect(const std::string &host, int port) {
  482. TRACE(2, "DebuggerClient::connect\n");
  483. assertx((!m_machines.empty() && m_machines[0]->m_name == LocalPrompt));
  484. // First check for an existing connect, and reuse that.
  485. for (unsigned int i = 1; i < m_machines.size(); i++) {
  486. if (HHVM_FN(gethostbyname)(m_machines[i]->m_name) ==
  487. HHVM_FN(gethostbyname)(host)) {
  488. switchMachine(m_machines[i]);
  489. return false;
  490. }
  491. }
  492. return connectRemote(host, port);
  493. }
  494. bool DebuggerClient::connectRPC(const std::string &host, int port) {
  495. TRACE(2, "DebuggerClient::connectRPC\n");
  496. assertx(!m_machines.empty());
  497. auto local = m_machines[0];
  498. assertx(local->m_name == LocalPrompt);
  499. local->m_rpcHost = host;
  500. local->m_rpcPort = port;
  501. switchMachine(local);
  502. m_rpcHost = "rpc:" + host;
  503. usageLogEvent("RPC connect", m_rpcHost);
  504. return !local->m_interrupting;
  505. }
  506. bool DebuggerClient::disconnect() {
  507. TRACE(2, "DebuggerClient::disconnect\n");
  508. assertx(!m_machines.empty());
  509. auto local = m_machines[0];
  510. assertx(local->m_name == LocalPrompt);
  511. local->m_rpcHost.clear();
  512. local->m_rpcPort = 0;
  513. switchMachine(local);
  514. return !local->m_interrupting;
  515. }
  516. void DebuggerClient::switchMachine(std::shared_ptr<DMachineInfo> machine) {
  517. TRACE(2, "DebuggerClient::switchMachine\n");
  518. m_rpcHost.clear();
  519. machine->m_initialized = false; // even if m_machine == machine
  520. if (m_machine != machine) {
  521. m_machine = machine;
  522. m_sandboxes.clear();
  523. m_threads.clear();
  524. m_threadId = 0;
  525. m_breakpoint.reset();
  526. m_matched.clear();
  527. m_listFile.clear();
  528. m_listLine = 0;
  529. m_listLineFocus = 0;
  530. m_stacktrace.reset();
  531. m_frame = 0;
  532. }
  533. }
  534. req::ptr<Socket> DebuggerClient::connectLocal() {
  535. TRACE(2, "DebuggerClient::connectLocal\n");
  536. int fds[2];
  537. if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
  538. throw Exception("unable to create socket pair for local debugging");
  539. }
  540. auto socket1 = req::make<StreamSocket>(fds[0], AF_UNIX);
  541. auto socket2 = req::make<StreamSocket>(fds[1], AF_UNIX);
  542. socket1->unregister();
  543. socket2->unregister();
  544. auto machine = std::make_shared<DMachineInfo>();
  545. machine->m_sandboxAttached = true;
  546. machine->m_name = LocalPrompt;
  547. machine->m_thrift.create(socket1);
  548. assertx(m_machines.empty());
  549. m_machines.push_back(machine);
  550. switchMachine(machine);
  551. return socket2;
  552. }
  553. bool DebuggerClient::connectRemote(const std::string &host, int port) {
  554. TRACE(2, "DebuggerClient::connectRemote\n");
  555. if (port <= 0) {
  556. port = RuntimeOption::DebuggerServerPort;
  557. }
  558. info("Connecting to %s:%d...", host.c_str(), port);
  559. if (tryConnect(host, port, false)) {
  560. return true;
  561. }
  562. error("Unable to connect to %s:%d.", host.c_str(), port);
  563. return false;
  564. }
  565. bool DebuggerClient::reconnect() {
  566. TRACE(2, "DebuggerClient::reconnect\n");
  567. assertx(m_machine);
  568. auto& host = m_machine->m_name;
  569. int port = m_machine->m_port;
  570. if (port <= 0) {
  571. return false;
  572. }
  573. info("Re-connecting to %s:%d...", host.c_str(), port);
  574. m_machine->m_thrift.close(); // Close the old socket, it may still be open.
  575. if (tryConnect(host, port, true)) {
  576. return true;
  577. }
  578. error("Still unable to connect to %s:%d.", host.c_str(), port);
  579. return false;
  580. }
  581. bool DebuggerClient::tryConnect(const std::string &host, int port,
  582. bool clearmachines) {
  583. struct addrinfo *ai;
  584. struct addrinfo hint;
  585. memset(&hint, 0, sizeof(hint));
  586. hint.ai_family = AF_UNSPEC;
  587. hint.ai_socktype = SOCK_STREAM;
  588. if (RuntimeOption::DebuggerDisableIPv6) {
  589. hint.ai_family = AF_INET;
  590. }
  591. if (getaddrinfo(host.c_str(), nullptr, &hint, &ai)) {
  592. return false;
  593. }
  594. SCOPE_EXIT {
  595. freeaddrinfo(ai);
  596. };
  597. /* try possible families (v4, v6) until we get a connection */
  598. struct addrinfo *cur;
  599. for (cur = ai; cur; cur = cur->ai_next) {
  600. auto sock = req::make<StreamSocket>(
  601. socket(cur->ai_family, cur->ai_socktype, 0),
  602. cur->ai_family,
  603. cur->ai_addr->sa_data,
  604. port
  605. );
  606. sock->unregister();
  607. if (HHVM_FN(socket_connect)(Resource(sock), String(host), port)) {
  608. if (clearmachines) {
  609. for (unsigned int i = 0; i < m_machines.size(); i++) {
  610. if (m_machines[i] == m_machine) {
  611. m_machines.erase(m_machines.begin() + i);
  612. break;
  613. }
  614. }
  615. }
  616. auto machine = std::make_shared<DMachineInfo>();
  617. machine->m_name = host;
  618. machine->m_port = port;
  619. machine->m_thrift.create(sock);
  620. m_machines.push_back(machine);
  621. switchMachine(machine);
  622. return true;
  623. }
  624. }
  625. return false;
  626. }
  627. std::string DebuggerClient::getPrompt() {
  628. TRACE(2, "DebuggerClient::getPrompt\n");
  629. if (NoPrompt || !RuntimeOption::EnableDebuggerPrompt) {
  630. return "";
  631. }
  632. auto name = &m_machine->m_name;
  633. if (!m_rpcHost.empty()) {
  634. name = &m_rpcHost;
  635. }
  636. if (m_inputState == TakingCode) {
  637. std::string prompt = " ";
  638. for (unsigned i = 2; i < name->size() + 2; i++) {
  639. prompt += '.';
  640. }
  641. prompt += ' ';
  642. return prompt;
  643. }
  644. return *name + "> ";
  645. }
  646. void DebuggerClient::init(const DebuggerClientOptions &options) {
  647. TRACE(2, "DebuggerClient::init\n");
  648. m_options = options;
  649. if (!options.configFName.empty()) {
  650. m_configFileName = options.configFName;
  651. }
  652. if (options.user.empty()) {
  653. m_options.user = Process::GetCurrentUser();
  654. }
  655. usageLogEvent("init");
  656. loadConfig();
  657. if (m_scriptMode) {
  658. print("running in script mode, pid=%" PRId64 "\n",
  659. (int64_t)getpid());
  660. }
  661. if (!options.cmds.empty()) {
  662. RuntimeOption::EnableDebuggerColor = false;
  663. RuntimeOption::EnableDebuggerPrompt = false;
  664. s_use_utf8 = false;
  665. }
  666. if (UseColor && RuntimeOption::EnableDebuggerColor) Debugger::SetTextColors();
  667. if (!NoPrompt && RuntimeOption::EnableDebuggerPrompt) {
  668. info("Welcome to HipHop Debugger!");
  669. info("Type \"help\" or \"?\" for a complete list of commands.\n");
  670. }
  671. if (!options.host.empty()) {
  672. connectRemote(options.host, options.port);
  673. } else {
  674. if (options.fileName.empty()) {
  675. help("Note: no server specified, debugging local scripts only.");
  676. help("If you want to connect to a server, launch with \"-h\" or use:");
  677. help(" [m]achine [c]onnect <servername>\n");
  678. }
  679. }
  680. }
  681. void DebuggerClient::start(const DebuggerClientOptions &options) {
  682. TRACE(2, "DebuggerClient::start\n");
  683. init(options);
  684. m_mainThread.start();
  685. }
  686. // Executed by m_mainThread to run the command-line debugger.
  687. void DebuggerClient::run() {
  688. TRACE(2, "DebuggerClient::run\n");
  689. StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
  690. ReadlineApp app;
  691. TRACE(3, "DebuggerClient::run, about to call playMacro\n");
  692. playMacro("startup");
  693. if (!m_options.cmds.empty()) {
  694. m_macroPlaying = std::make_shared<Macro>();
  695. m_macroPlaying->m_cmds = m_options.cmds;
  696. m_macroPlaying->m_cmds.push_back("q");
  697. m_macroPlaying->m_index = 0;
  698. }
  699. hphp_session_init(Treadmill::SessionKind::DebuggerClient);
  700. if (m_options.extension.empty()) {
  701. hphp_invoke_simple("", true); // warm-up only
  702. } else {
  703. hphp_invoke_simple(m_options.extension, false);
  704. }
  705. while (true) {
  706. bool reconnect = false;
  707. try {
  708. eventLoop(TopLevel, DebuggerCommand::KindOfNone, "Main client loop");
  709. } catch (DebuggerClientExitException& e) { /* normal exit */
  710. } catch (DebuggerServerLostException& e) {
  711. // Loss of connection
  712. TRACE_RB(1, "DebuggerClient::run: server lost exception\n");
  713. usageLogEvent("DebuggerServerLostException", m_commandCanonical);
  714. reconnect = true;
  715. } catch (DebuggerProtocolException& e) {
  716. // Bad or unexpected data. Give reconnect a shot, it could help...
  717. TRACE_RB(1, "DebuggerClient::run: protocol exception\n");
  718. usageLogEvent("DebuggerProtocolException", m_commandCanonical);
  719. reconnect = true;
  720. } catch (...) {
  721. TRACE_RB(1, "DebuggerClient::run: unknown exception\n");
  722. usageLogEvent("UnknownException", m_commandCanonical);
  723. Logger::Error("Unhandled exception, exiting.");
  724. }
  725. // Note: it's silly to try to reconnect when stopping, or if we have a
  726. // problem while quitting.
  727. if (reconnect && !m_stopped && (m_commandCanonical != "quit")) {
  728. usageLogEvent("reconnect attempt", m_commandCanonical);
  729. if (DebuggerClient::reconnect()) {
  730. usageLogEvent("reconnect success", m_commandCanonical);
  731. continue;
  732. }
  733. usageLogEvent("reconnect failed", m_commandCanonical);
  734. Logger::Error("Unable to reconnect to server, exiting.");
  735. }
  736. break;
  737. }
  738. usageLogEvent("exit");
  739. // Closing all proxy connections will force the local proxy to pop out of
  740. // it's wait, and eventually exit the main thread.
  741. closeAllConnections();
  742. hphp_context_exit();
  743. hphp_session_exit();
  744. }
  745. ///////////////////////////////////////////////////////////////////////////////
  746. // auto-complete
  747. void DebuggerClient::updateLiveLists() {
  748. TRACE(2, "DebuggerClient::updateLiveLists\n");
  749. ReadlineWaitCursor waitCursor;
  750. CmdInfo::UpdateLiveLists(*this);
  751. m_acLiveListsDirty = false;
  752. }
  753. void DebuggerClient::promptFunctionPrototype() {
  754. TRACE(2, "DebuggerClient::promptFunctionPrototype\n");
  755. if (m_acProtoTypePrompted) return;
  756. m_acProtoTypePrompted = true;
  757. const char *p0 = rl_line_buffer;
  758. int len = strlen(p0);
  759. if (len < 2) return;
  760. const char *pLast = p0 + len - 1;
  761. while (pLast > p0 && isspace(*pLast)) --pLast;
  762. if (pLast == p0 || *pLast-- != '(') return;
  763. while (pLast > p0 && isspace(*pLast)) --pLast;
  764. const char *p = pLast;
  765. while (p >= p0 && (isalnum(*p) || *p == '_')) --p;
  766. if (p == pLast) return;
  767. std::string cls;
  768. std::string func(p + 1, pLast - p);
  769. if (p > p0 && *p-- == ':' && *p-- == ':') {
  770. pLast = p;
  771. while (p >= p0 && (isalnum(*p) || *p == '_')) --p;
  772. if (pLast > p) {
  773. cls = std::string(p + 1, pLast - p);
  774. }
  775. }
  776. String output = highlight_code(CmdInfo::GetProtoType(*this, cls, func));
  777. print("\n%s", output.data());
  778. rl_forced_update_display();
  779. }
  780. bool DebuggerClient::setCompletion(const char* text, int /*start*/,
  781. int /*end*/) {
  782. TRACE(2, "DebuggerClient::setCompletion\n");
  783. if (m_inputState == TakingCommand) {
  784. parseCommand(rl_line_buffer);
  785. if (*text) {
  786. if (!m_args.empty()) {
  787. m_args.resize(m_args.size() - 1);
  788. } else {
  789. m_command.clear();
  790. }
  791. }
  792. }
  793. return true;
  794. }
  795. void DebuggerClient::addCompletion(AutoComplete type) {
  796. TRACE(2, "DebuggerClient::addCompletion(AutoComplete type)\n");
  797. if (type < 0 || type >= AutoCompleteCount) {
  798. Logger::Error("Invalid auto completion enum: %d", type);
  799. return;
  800. }
  801. if (type == AutoCompleteCode) {
  802. addCompletion(AutoCompleteVariables);
  803. addCompletion(AutoCompleteConstants);
  804. addCompletion(AutoCompleteClasses);
  805. addCompletion(AutoCompleteFunctions);
  806. addCompletion(AutoCompleteClassMethods);
  807. addCompletion(AutoCompleteClassProperties);
  808. addCompletion(AutoCompleteClassConstants);
  809. addCompletion(AutoCompleteKeyword);
  810. } else if (type == AutoCompleteKeyword) {
  811. addCompletion(PHP_KEYWORDS);
  812. } else {
  813. m_acLists.push_back((const char **)type);
  814. }
  815. if (type == AutoCompleteFunctions || type == AutoCompleteClassMethods) {
  816. promptFunctionPrototype();
  817. }
  818. }
  819. void DebuggerClient::addCompletion(const char** list) {
  820. TRACE(2, "DebuggerClient::addCompletion(const char **list)\n");
  821. m_acLists.push_back(list);
  822. }
  823. void DebuggerClient::addCompletion(const char* name) {
  824. TRACE(2, "DebuggerClient::addCompletion(const char *name)\n");
  825. m_acStrings.push_back(name);
  826. }
  827. void DebuggerClient::addCompletion(const std::vector<std::string>& items) {
  828. TRACE(2, "DebuggerClient::addCompletion(const std::vector<std::string>)\n");
  829. m_acItems.insert(m_acItems.end(), items.begin(), items.end());
  830. }
  831. char* DebuggerClient::getCompletion(const std::vector<std::string>& items,
  832. const char* text) {
  833. TRACE(2, "DebuggerClient::getCompletion(const std::vector<std::string>\n");
  834. while (++m_acPos < (int)items.size()) {
  835. auto const p = items[m_acPos].c_str();
  836. if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
  837. return strdup(p);
  838. }
  839. }
  840. m_acPos = -1;
  841. return nullptr;
  842. }
  843. std::vector<std::string> DebuggerClient::getAllCompletions(
  844. const std::string& text
  845. ) {
  846. TRACE(2, "DebuggerClient::getAllCompletions\n");
  847. std::vector<std::string> res;
  848. if (m_acLiveListsDirty) {
  849. updateLiveLists();
  850. }
  851. for (int i = 0; i < AutoCompleteCount; ++i) {
  852. auto const& items = m_acLiveLists->get(i);
  853. for (size_t j = 0; j < items.size(); ++j) {
  854. auto const p = items[j].c_str();
  855. if (strncasecmp(p, text.c_str(), text.length()) == 0) {
  856. res.push_back(std::string(p));
  857. }
  858. }
  859. }
  860. return res;
  861. }
  862. char* DebuggerClient::getCompletion(const std::vector<const char*>& items,
  863. const char* text) {
  864. TRACE(2, "DebuggerClient::getCompletion(const std::vector<const char *>\n");
  865. while (++m_acPos < (int)items.size()) {
  866. auto const p = items[m_acPos];
  867. if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
  868. return strdup(p);
  869. }
  870. }
  871. m_acPos = -1;
  872. return nullptr;
  873. }
  874. static char first_non_whitespace(const char* s) {
  875. TRACE(2, "DebuggerClient::first_non_whitespace\n");
  876. while (*s && isspace(*s)) s++;
  877. return *s;
  878. }
  879. char* DebuggerClient::getCompletion(const char* text, int state) {
  880. TRACE(2, "DebuggerClient::getCompletion\n");
  881. if (state == 0) {
  882. m_acLen = strlen(text);
  883. m_acIndex = 0;
  884. m_acPos = -1;
  885. m_acLists.clear();
  886. m_acStrings.clear();
  887. m_acItems.clear();
  888. m_acProtoTypePrompted = false;
  889. if (m_inputState == TakingCommand) {
  890. switch (first_non_whitespace(rl_line_buffer)) {
  891. case '<':
  892. if (strncasecmp(m_command.substr(0, 5).c_str(), "<?php", 5)) {
  893. addCompletion("<?php");
  894. break;
  895. }
  896. case '@':
  897. case '=':
  898. case '$': {
  899. addCompletion(AutoCompleteCode);
  900. break;
  901. }
  902. default: {
  903. if (m_command.empty()) {
  904. addCompletion(GetCommands());
  905. addCompletion("@");
  906. addCompletion("=");
  907. addCompletion("<?php");
  908. addCompletion("?>");
  909. } else {
  910. auto cmd = createCommand();
  911. if (cmd) {
  912. if (cmd->is(DebuggerCommand::KindOfRun)) playMacro("startup");
  913. cmd->list(*this);
  914. }
  915. }
  916. break;
  917. }
  918. }
  919. } else {
  920. assertx(m_inputState == TakingCode);
  921. if (!*rl_line_buffer) {
  922. addCompletion("?>"); // so we tab, we're done
  923. } else {
  924. addCompletion(AutoCompleteCode);
  925. }
  926. }
  927. }
  928. for (; m_acIndex < (int)m_acLists.size(); m_acIndex++) {
  929. const char **list = m_acLists[m_acIndex];
  930. if ((int64_t)list == AutoCompleteFileNames) {
  931. char *p = rl_filename_completion_function(text, ++m_acPos);
  932. if (p) return p;
  933. } else if ((int64_t)list >= 0 && (int64_t)list < AutoCompleteCount) {
  934. if (m_acLiveListsDirty) {
  935. updateLiveLists();
  936. assertx(!m_acLiveListsDirty);
  937. }
  938. char *p = getCompletion(m_acLiveLists->get(int64_t(list)), text);
  939. if (p) return p;
  940. } else {
  941. for (const char *p = list[++m_acPos]; p; p = list[++m_acPos]) {
  942. if (m_acLen == 0 || strncasecmp(p, text, m_acLen) == 0) {
  943. return strdup(p);
  944. }
  945. }
  946. }
  947. m_acPos = -1;
  948. }
  949. char *p = getCompletion(m_acStrings, text);
  950. if (p) return p;
  951. return getCompletion(m_acItems, text);
  952. }
  953. ///////////////////////////////////////////////////////////////////////////////
  954. // main
  955. // Execute the initial connection protocol with a machine. A connection has been
  956. // established, and the proxy has responded with an interrupt giving us initial
  957. // control. Send breakpoints to the server, and then attach to the sandbox
  958. // if necessary. If we attach to a sandbox, then the process is off and running
  959. // again (CmdMachine continues execution on a successful attach) so return false
  960. // to indicate that a client should wait for another interrupt before attempting
  961. // further communication. Returns true if the protocol is complete and the
  962. // machine is at an interrupt.
  963. bool DebuggerClient::initializeMachine() {
  964. TRACE(2, "DebuggerClient::initializeMachine\n");
  965. // set/clear intercept for RPC thread
  966. if (!m_machines.empty() && m_machine == m_machines[0]) {
  967. CmdMachine::UpdateIntercept(*this, m_machine->m_rpcHost,
  968. m_machine->m_rpcPort);
  969. }
  970. // upload breakpoints
  971. if (!m_breakpoints.empty()) {
  972. info("Updating breakpoints...");
  973. CmdBreak::SendClientBreakpointListToServer(*this);
  974. }
  975. // attaching to default sandbox
  976. int waitForSandbox = false;
  977. if (!m_machine->m_sandboxAttached) {
  978. const char *user = m_options.user.empty() ?
  979. nullptr : m_options.user.c_str();
  980. m_machine->m_sandboxAttached = (waitForSandbox =
  981. CmdMachine::AttachSandbox(*this, user, m_options.sandbox.c_str()));
  982. if (!m_machine->m_sandboxAttached) {
  983. Logger::Error("Unable to communicate with default sandbox.");
  984. }
  985. }
  986. m_machine->m_initialized = true;
  987. if (waitForSandbox) {
  988. // Return false to wait for next interrupt from server
  989. return false;
  990. }
  991. return true;
  992. }
  993. // The main execution loop of DebuggerClient. This waits for interrupts from
  994. // the server (and responds to polls for signals). On interrupt, it presents a
  995. // command prompt, and continues pumping interrupts when a command lets the
  996. // machine run again. For nested loops it returns the command that completed
  997. // the loop, which will match the expectedCmd passed in. For all loop types,
  998. // throws one of a variety of exceptions for various errors, and throws
  999. // DebuggerClientExitException when the event loop is terminated due to the
  1000. // client stopping.
  1001. DebuggerCommandPtr DebuggerClient::eventLoop(EventLoopKind loopKind,
  1002. int expectedCmd,
  1003. const char *caller) {
  1004. TRACE(2, "DebuggerClient::eventLoop\n");
  1005. ARRPROV_USE_RUNTIME_LOCATION();
  1006. if (loopKind == NestedWithExecution) {
  1007. // Some callers have caused the server to start executing more PHP, so
  1008. // update the machine/client state accordingly.
  1009. m_inputState = TakingInterrupt;
  1010. m_machine->m_interrupting = false;
  1011. }
  1012. while (!m_stopped) {
  1013. DebuggerCommandPtr cmd;
  1014. if (DebuggerCommand::Receive(m_machine->m_thrift, cmd, caller)) {
  1015. if (!cmd) {
  1016. Logger::Error("Unable to communicate with server. Server's down?");
  1017. throw DebuggerServerLostException();
  1018. }
  1019. if (cmd->is(DebuggerCommand::KindOfSignal) ||
  1020. cmd->is(DebuggerCommand::KindOfAuth)) {
  1021. // Respond to polling from the server.
  1022. cmd->onClient(*this);
  1023. continue;
  1024. }
  1025. if (!cmd->getWireError().empty()) {
  1026. error("wire error: %s", cmd->getWireError().data());
  1027. }
  1028. if ((loopKind != TopLevel) &&
  1029. cmd->is((DebuggerCommand::Type)expectedCmd)) {
  1030. // For the nested cases, the caller has sent a cmd to the server and is
  1031. // expecting a specific response. When we get it, return it.
  1032. usageLogEvent("command done", folly::to<std::string>(expectedCmd));
  1033. m_machine->m_interrupting = true; // Machine is stopped
  1034. m_inputState = TakingCommand;
  1035. return cmd;
  1036. }
  1037. if ((loopKind == Nested) || !cmd->is(DebuggerCommand::KindOfInterrupt)) {
  1038. Logger::Error("Received bad cmd type %d, unable to communicate "
  1039. "with server.", cmd->getType());
  1040. throw DebuggerProtocolException();
  1041. }
  1042. m_sigCount = 0;
  1043. auto intr = std::dynamic_pointer_cast<CmdInterrupt>(cmd);
  1044. Debugger::UsageLogInterrupt("terminal", getSandboxId(), *intr.get());
  1045. cmd->onClient(*this);
  1046. // When we make a new connection to a machine, we have to wait for it
  1047. // to interrupt us before we can send it any messages. This is our
  1048. // opportunity to complete the connection and make it ready to use.
  1049. if (!m_machine->m_initialized) {
  1050. if (!initializeMachine()) {
  1051. // False means the machine is running and we need to wait for
  1052. // another interrupt.
  1053. continue;
  1054. }
  1055. }
  1056. // Execution has been interrupted, so go ahead and give the user
  1057. // the prompt back.
  1058. m_machine->m_interrupting = true; // Machine is stopped
  1059. m_inputState = TakingCommand;
  1060. console(); // Prompt loop
  1061. m_inputState = TakingInterrupt;
  1062. m_machine->m_interrupting = false; // Machine is running again.
  1063. if (m_scriptMode) {
  1064. print("Waiting for server response");
  1065. }
  1066. }
  1067. }
  1068. throw DebuggerClientExitException(); // Stopped, so exit.
  1069. }
  1070. // Execute the interactive command loop for the debugger client. This will
  1071. // present the prompt, wait for user input, and execute commands, then rinse
  1072. // and repeat. The loop terminates when a command is executed that causes the
  1073. // machine to resume execution, or which should cause the client to exit.
  1074. // This function is only entered when the machine being debugged is paused.
  1075. //
  1076. // If this function returns it means the process is running again.
  1077. // NB: exceptions derived from DebuggerException or DebuggerClientExeption
  1078. // indicate the machine remains paused.
  1079. void DebuggerClient::console() {
  1080. TRACE(2, "DebuggerClient::console\n");
  1081. while (true) {
  1082. const char *line = nullptr;
  1083. std::string holder;
  1084. if (m_macroPlaying) {
  1085. if (m_macroPlaying->m_index < m_macroPlaying->m_cmds.size()) {
  1086. holder = m_macroPlaying->m_cmds[m_macroPlaying->m_index++];
  1087. line = holder.c_str();
  1088. } else {
  1089. m_macroPlaying.reset();
  1090. }
  1091. }
  1092. if (line == nullptr) {
  1093. line = readline(getPrompt().c_str());
  1094. if (line == nullptr) {
  1095. // treat ^D as quit
  1096. print("quit");
  1097. line = "quit";
  1098. } else {
  1099. #ifdef USE_EDITLINE
  1100. print("%s", line); // Stay consistent with the readline library
  1101. #endif
  1102. }
  1103. } else if (!NoPrompt && RuntimeOption::EnableDebuggerPrompt) {
  1104. print("%s%s", getPrompt().c_str(), line);
  1105. }
  1106. if (*line && !m_macroPlaying &&
  1107. strcasecmp(line, "QUIT") != 0 &&
  1108. strcasecmp(line, "QUI") != 0 &&
  1109. strcasecmp(line, "QU") != 0 &&
  1110. strcasecmp(line, "Q") != 0) {
  1111. // even if line is bad command, we still want to remember it, so
  1112. // people can go back and fix typos
  1113. HIST_ENTRY *last_entry = nullptr;
  1114. if (history_length > 0 &&
  1115. (last_entry = history_get(history_length + history_base - 1))) {
  1116. // Make sure we aren't duplicating history entries
  1117. if (strcmp(line, last_entry->line)) {
  1118. add_history(line);
  1119. }
  1120. } else {
  1121. // Add history regardless, since we know that there are no
  1122. // duplicate entries.
  1123. add_history(line);
  1124. }
  1125. }
  1126. AdjustScreenMetrics();
  1127. if (*line) {
  1128. if (parse(line)) {
  1129. try {
  1130. record(line);
  1131. m_prevCmd = m_command;
  1132. if (!process()) {
  1133. error("command \"" + m_command + "\" not found");
  1134. m_command.clear();
  1135. }
  1136. } catch (DebuggerConsoleExitException& e) {
  1137. return;
  1138. }
  1139. }
  1140. } else if (m_inputState == TakingCommand) {
  1141. switch (m_prevCmd[0]) {
  1142. case 'l': // list
  1143. m_args.clear(); // as if just "list"
  1144. // fall through
  1145. case 'c': // continue
  1146. case 's': // step
  1147. case 'n': // next
  1148. case 'o': // out
  1149. try {
  1150. record(line);
  1151. m_command = m_prevCmd;
  1152. process(); // replay the same command
  1153. } catch (DebuggerConsoleExitException& e) {
  1154. return;
  1155. }
  1156. break;
  1157. }
  1158. }
  1159. }
  1160. not_reached();
  1161. }
  1162. const StaticString
  1163. s_file("file"),
  1164. s_line("line");
  1165. //
  1166. // Called when a breakpoint is reached, to produce the console
  1167. // spew showing the code around the breakpoint.
  1168. //
  1169. void DebuggerClient::shortCode(BreakPointInfoPtr bp) {
  1170. TRACE(2, "DebuggerClient::shortCode\n");
  1171. if (bp && !bp->m_file.empty() && bp->m_line1) {
  1172. Variant source = CmdList::GetSourceFile(*this, bp->m_file);
  1173. if (source.isString()) {
  1174. // Line and column where highlight should start and end
  1175. int beginHighlightLine = bp->m_line1;
  1176. int beginHighlightColumn = bp->m_char1;
  1177. int endHighlightLine = bp->m_line2;
  1178. int endHighlightColumn = bp->m_char2;
  1179. // Lines where source listing should start and end
  1180. int firstLine = std::max(beginHighlightLine - 1, 1);
  1181. int lastLine = endHighlightLine + 1;
  1182. int maxLines = getDebuggerClientMaxCodeLines();
  1183. // If MaxCodeLines == 0: don't spew any code after a [s]tep or [n]ext
  1184. // command.
  1185. if (maxLines == 0) {
  1186. return;
  1187. }
  1188. // If MaxCodeLines > 0: limit spew to a maximum of # lines.
  1189. if (maxLines > 0) {
  1190. int numHighlightLines = endHighlightLine - beginHighlightLine + 1;
  1191. if (numHighlightLines > maxLines) {
  1192. // If there are too many highlight lines, truncate spew
  1193. // by setting lastLine ...
  1194. lastLine = beginHighlightLine + maxLines - 1;
  1195. // ... and set endHighlightLine/Column so that it is just past the end
  1196. // of the spew, and all code up to the truncation will be highlighted.
  1197. endHighlightLine = lastLine + 1;
  1198. endHighlightColumn = 1;
  1199. }
  1200. }
  1201. code(source.toString(), firstLine, lastLine,
  1202. beginHighlightLine,
  1203. beginHighlightColumn,
  1204. endHighlightLine,
  1205. endHighlightColumn);
  1206. }
  1207. }
  1208. }
  1209. bool DebuggerClient::code(const String& source, int line1 /*= 0*/,
  1210. int line2 /*= 0*/,
  1211. int lineFocus0 /* = 0 */, int charFocus0 /* = 0 */,
  1212. int lineFocus1 /* = 0 */, int charFocus1 /* = 0 */) {
  1213. TRACE(2, "DebuggerClient::code\n");
  1214. if (line1 == 0 && line2 == 0) {
  1215. String highlighted = highlight_code(source, 0, lineFocus0, charFocus0,
  1216. lineFocus1, charFocus1);
  1217. if (!highlighted.empty()) {
  1218. print(highlighted);
  1219. return true;
  1220. }
  1221. return false;
  1222. }
  1223. String highlighted = highlight_php(source, 1, lineFocus0, charFocus0,
  1224. lineFocus1, charFocus1);
  1225. int line = 1;
  1226. const char *begin = highlighted.data();
  1227. StringBuffer sb;
  1228. for (const char *p = begin; *p; p++) {
  1229. if (*p == '\n') {
  1230. if (line >= line1) {
  1231. sb.append(begin, p - begin + 1);
  1232. }
  1233. if (++line > line2) break;
  1234. begin = p + 1;
  1235. }
  1236. }
  1237. if (!sb.empty()) {
  1238. print("%s%s", sb.data(),
  1239. UseColor && RuntimeOption::EnableDebuggerColor ? ANSI_COLOR_END : "\0");
  1240. return true;
  1241. }
  1242. return false;
  1243. }
  1244. char DebuggerClient::ask(const char *fmt, ...) {
  1245. TRACE(2, "DebuggerClient::ask\n");
  1246. std::string msg;
  1247. va_list ap;
  1248. va_start(ap, fmt);
  1249. string_vsnprintf(msg, fmt, ap); va_end(ap);
  1250. if (UseColor && InfoColor && RuntimeOption::EnableDebuggerColor) {
  1251. msg = InfoColor + msg + ANSI_COLOR_END;
  1252. }
  1253. fwrite(msg.data(), 1, msg.length(), stdout);
  1254. fflush(stdout);
  1255. auto input = readline("");
  1256. if (input == nullptr) return ' ';
  1257. #ifdef USE_EDITLINE
  1258. print("%s", input); // Stay consistent with the readline library
  1259. #endif
  1260. if (strlen(input) > 0) return tolower(input[0]);
  1261. return ' ';
  1262. }
  1263. #define DWRITE(ptr, size, nmemb, stream) \
  1264. do { \
  1265. /* LogFile debugger setting */ \
  1266. FILE *f = getLogFileHandler(); \
  1267. if (f != nullptr) { \
  1268. fwrite(ptr, size, nmemb, f); \
  1269. } \
  1270. \
  1271. /* For debugging, still output to stdout */ \
  1272. fwrite(ptr, size, nmemb, stream); \
  1273. } while (0) \
  1274. void DebuggerClient::print(const char* fmt, ...) {
  1275. TRACE(2, "DebuggerClient::print(const char* fmt, ...)\n");
  1276. std::string msg;
  1277. va_list ap;
  1278. va_start(ap, fmt);
  1279. string_vsnprintf(msg, fmt, ap); va_end(ap);
  1280. print(msg);
  1281. }
  1282. void DebuggerClient::print(const String& msg) {
  1283. TRACE(2, "DebuggerClient::print(const String& msg)\n");
  1284. DWRITE(msg.data(), 1, msg.length(), stdout);
  1285. DWRITE("\n", 1, 1, stdout);
  1286. fflush(stdout);
  1287. }
  1288. void DebuggerClient::print(const std::string& msg) {
  1289. TRACE(2, "DebuggerClient::print(const std::string& msg)\n");
  1290. DWRITE(msg.data(), 1, msg.size(), stdout);
  1291. DWRITE("\n", 1, 1, stdout);
  1292. fflush(stdout);
  1293. }
  1294. void DebuggerClient::print(folly::StringPiece msg) {
  1295. TRACE(2, "DebuggerClient::print(folly::StringPiece msg)\n");
  1296. DWRITE(msg.data(), 1, msg.size(), stdout);
  1297. DWRITE("\n", 1, 1, stdout);
  1298. fflush(stdout);
  1299. }
  1300. #define IMPLEMENT_COLOR_OUTPUT(name, where, color) \
  1301. void DebuggerClient::name(folly::StringPiece msg) { \
  1302. if (UseColor && color && RuntimeOption::EnableDebuggerColor) { \
  1303. DWRITE(color, 1, strlen(color), where); \
  1304. } \
  1305. DWRITE(msg.data(), 1, msg.size(), where); \
  1306. if (UseColor && color && RuntimeOption::EnableDebuggerColor) { \
  1307. DWRITE(ANSI_COLOR_END, 1, strlen(ANSI_COLOR_END), where); \
  1308. } \
  1309. DWRITE("\n", 1, 1, where); \
  1310. fflush(where); \
  1311. } \
  1312. \
  1313. void DebuggerClient::name(const String& msg) { \
  1314. name(msg.slice()); \
  1315. } \
  1316. \
  1317. void DebuggerClient::name(const std::string& msg) { \
  1318. name(folly::StringPiece{msg}); \
  1319. } \
  1320. \
  1321. void DebuggerClient::name(const char *fmt, ...) { \
  1322. std::string msg; \
  1323. va_list ap; \
  1324. va_start(ap, fmt); \
  1325. string_vsnprintf(msg, fmt, ap); va_end(ap); \
  1326. name(msg); \
  1327. } \
  1328. IMPLEMENT_COLOR_OUTPUT(help, stdout, HelpColor);
  1329. IMPLEMENT_COLOR_OUTPUT(info, stdout, InfoColor);
  1330. IMPLEMENT_COLOR_OUTPUT(output, stdout, OutputColor);
  1331. IMPLEMENT_COLOR_OUTPUT(error, stderr, ErrorColor);
  1332. #undef DWRITE
  1333. #undef IMPLEMENT_COLOR_OUTPUT
  1334. std::string DebuggerClient::wrap(const std::string &s) {
  1335. TRACE(2, "DebuggerClient::wrap\n");
  1336. String ret = wordwrap(String(s.c_str(), s.size(), CopyString), LineWidth - 4,
  1337. "\n", true);
  1338. return std::string(ret.data(), ret.size());
  1339. }
  1340. void DebuggerClient::helpTitle(const char *title) {
  1341. TRACE(2, "DebuggerClient::helpTitle\n");
  1342. help(FormatTitle(title));
  1343. }
  1344. void DebuggerClient::helpCmds(const char *cmd, const char *desc, ...) {
  1345. TRACE(2, "DebuggerClient::helpCmds(const char *cmd, const char *desc,...)\n");
  1346. std::vector<const char *> cmds;
  1347. cmds.push_back(cmd);
  1348. cmds.push_back(desc);
  1349. va_list ap;
  1350. va_start(ap

Large files files are truncated, but you can click here to view the full file