JSDebugger.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. #include "JSVM.h"
  2. #include "JSDebugger.h"
  3. // forward declaratons
  4. static void duk_trans_socket_init(void);
  5. static void duk_trans_socket_waitconn(void);
  6. static duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length);
  7. static duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length);
  8. static duk_size_t duk_trans_socket_peek_cb(void *udata);
  9. static void duk_trans_socket_read_flush_cb(void *udata);
  10. static void duk_trans_socket_write_flush_cb(void *udata);
  11. static void debugger_detached (duk_context* ctx, void *udata);
  12. static void duk_trans_socket_finish(void);
  13. namespace Atomic
  14. {
  15. JSDebugger* JSDebugger::instance_ = NULL;
  16. JSDebugger::JSDebugger(Context* context) :
  17. Object(context),
  18. autoReconnect_(true)
  19. {
  20. instance_ = this;
  21. }
  22. JSDebugger::~JSDebugger()
  23. {
  24. duk_trans_socket_finish();
  25. instance_ = NULL;
  26. }
  27. void JSDebugger::Shutdown() const
  28. {
  29. if (Atomic::JSDebugger::GetInstance())
  30. {
  31. Atomic::JSDebugger::GetInstance()->SetAutoReconnect(false);
  32. }
  33. }
  34. void JSDebugger::Reconnect() const
  35. {
  36. duk_trans_socket_init();
  37. duk_trans_socket_waitconn();
  38. JSVM* vm = JSVM::GetJSVM(0);
  39. if(!vm)
  40. {
  41. return;
  42. }
  43. duk_context *ctx_ = vm->GetJSContext();
  44. if(!ctx_)
  45. {
  46. return;
  47. }
  48. duk_debugger_attach(ctx_,
  49. duk_trans_socket_read_cb,
  50. duk_trans_socket_write_cb,
  51. duk_trans_socket_peek_cb,
  52. duk_trans_socket_read_flush_cb,
  53. duk_trans_socket_write_flush_cb,
  54. NULL, // duk_debug_request_function - no custom application specific request functions
  55. debugger_detached,
  56. NULL);
  57. }
  58. }
  59. static void do_reconnect()
  60. {
  61. Atomic::JSDebugger* dbg = Atomic::JSDebugger::GetInstance();
  62. if (dbg && dbg->GetAutoReconnect())
  63. {
  64. dbg->Reconnect();
  65. }
  66. }
  67. #ifdef ATOMIC_PLATFORM_WINDOWS
  68. /*
  69. * Example debug transport using a Windows TCP socket
  70. *
  71. * Provides a TCP server socket which a debug client can connect to.
  72. * After that data is just passed through.
  73. *
  74. * https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx
  75. *
  76. * Compiling 'duk' with debugger support using MSVC (Visual Studio):
  77. *
  78. * > python2 tools\configure.py \
  79. * --output-directory prep
  80. * -DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER
  81. * > cl /W3 /O2 /Feduk.exe \
  82. * /DDUK_CMDLINE_DEBUGGER_SUPPORT
  83. * /Iexamples\debug-trans-socket /Iprep
  84. * examples\cmdline\duk_cmdline.c
  85. * examples\debug-trans-socket\duk_trans_socket_windows.c
  86. * prep\duktape.c
  87. *
  88. * With MinGW:
  89. *
  90. * $ python2 tools\configure.py \
  91. * --output-directory prep
  92. * -DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER
  93. * $ gcc -oduk.exe -Wall -O2 \
  94. * -DDUK_CMDLINE_DEBUGGER_SUPPORT \
  95. * -Iexamples/debug-trans-socket -Iprep \
  96. * examples/cmdline/duk_cmdline.c \
  97. * examples/debug-trans-socket/duk_trans_socket_windows.c \
  98. * prep/duktape.c -lm -lws2_32
  99. */
  100. #undef UNICODE
  101. #if !defined(WIN32_LEAN_AND_MEAN)
  102. #define WIN32_LEAN_AND_MEAN
  103. #endif
  104. /* MinGW workaround for missing getaddrinfo() etc:
  105. * http://programmingrants.blogspot.fi/2009/09/tips-on-undefined-reference-to.html
  106. */
  107. #if defined(__MINGW32__) || defined(__MINGW64__)
  108. #if !defined(_WIN32_WINNT)
  109. #define _WIN32_WINNT 0x0501
  110. #endif
  111. #endif
  112. #include <windows.h>
  113. #include <winsock2.h>
  114. #include <ws2tcpip.h>
  115. #include <stdio.h>
  116. #include <string.h>
  117. #include <ThirdParty/Duktape/duktape.h>
  118. #if defined(_MSC_VER)
  119. #pragma comment (lib, "Ws2_32.lib")
  120. #endif
  121. #if !defined(DUK_DEBUG_PORT)
  122. #define DUK_DEBUG_PORT 9091
  123. #endif
  124. #if !defined(DUK_DEBUG_ADDRESS)
  125. #define DUK_DEBUG_ADDRESS "0.0.0.0"
  126. #endif
  127. #define DUK__STRINGIFY_HELPER(x) #x
  128. #define DUK__STRINGIFY(x) DUK__STRINGIFY_HELPER(x)
  129. #if 0
  130. #define DEBUG_PRINTS
  131. #endif
  132. static SOCKET server_sock = INVALID_SOCKET;
  133. static SOCKET client_sock = INVALID_SOCKET;
  134. static int wsa_inited = 0;
  135. /*
  136. * Transport init and finish
  137. */
  138. void duk_trans_socket_init(void) {
  139. WSADATA wsa_data;
  140. struct addrinfo hints;
  141. struct addrinfo *result = NULL;
  142. int rc;
  143. memset((void *) &wsa_data, 0, sizeof(wsa_data));
  144. memset((void *) &hints, 0, sizeof(hints));
  145. rc = WSAStartup(MAKEWORD(2, 2), &wsa_data);
  146. if (rc != 0) {
  147. fprintf(stderr, "%s: WSAStartup() failed: %d\n", __FILE__, rc);
  148. fflush(stderr);
  149. goto fail;
  150. }
  151. wsa_inited = 1;
  152. hints.ai_family = AF_UNSPEC;
  153. hints.ai_socktype = SOCK_STREAM;
  154. hints.ai_protocol = IPPROTO_TCP;
  155. hints.ai_flags = AI_PASSIVE;
  156. rc = getaddrinfo(DUK_DEBUG_ADDRESS, DUK__STRINGIFY(DUK_DEBUG_PORT), &hints, &result);
  157. if (rc != 0) {
  158. fprintf(stderr, "%s: getaddrinfo() failed: %d\n", __FILE__, rc);
  159. fflush(stderr);
  160. goto fail;
  161. }
  162. server_sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
  163. if (server_sock == INVALID_SOCKET) {
  164. fprintf(stderr, "%s: socket() failed with error: %ld\n",
  165. __FILE__, (long) WSAGetLastError());
  166. fflush(stderr);
  167. goto fail;
  168. }
  169. rc = bind(server_sock, result->ai_addr, (int) result->ai_addrlen);
  170. if (rc == SOCKET_ERROR) {
  171. fprintf(stderr, "%s: bind() failed with error: %ld\n",
  172. __FILE__, (long) WSAGetLastError());
  173. fflush(stderr);
  174. goto fail;
  175. }
  176. rc = listen(server_sock, SOMAXCONN);
  177. if (rc == SOCKET_ERROR) {
  178. fprintf(stderr, "%s: listen() failed with error: %ld\n",
  179. __FILE__, (long) WSAGetLastError());
  180. fflush(stderr);
  181. goto fail;
  182. }
  183. if (result != NULL) {
  184. freeaddrinfo(result);
  185. result = NULL;
  186. }
  187. return;
  188. fail:
  189. if (result != NULL) {
  190. freeaddrinfo(result);
  191. result = NULL;
  192. }
  193. if (server_sock != INVALID_SOCKET) {
  194. (void) closesocket(server_sock);
  195. server_sock = INVALID_SOCKET;
  196. }
  197. if (wsa_inited) {
  198. WSACleanup();
  199. wsa_inited = 0;
  200. }
  201. }
  202. void duk_trans_socket_finish(void) {
  203. if (client_sock != INVALID_SOCKET) {
  204. (void) closesocket(client_sock);
  205. client_sock = INVALID_SOCKET;
  206. }
  207. if (server_sock != INVALID_SOCKET) {
  208. (void) closesocket(server_sock);
  209. server_sock = INVALID_SOCKET;
  210. }
  211. if (wsa_inited) {
  212. WSACleanup();
  213. wsa_inited = 0;
  214. }
  215. // try to auto-restart
  216. do_reconnect();
  217. }
  218. void duk_trans_socket_waitconn(void) {
  219. if (server_sock == INVALID_SOCKET) {
  220. fprintf(stderr, "%s: no server socket, skip waiting for connection\n",
  221. __FILE__);
  222. fflush(stderr);
  223. return;
  224. }
  225. if (client_sock != INVALID_SOCKET) {
  226. (void) closesocket(client_sock);
  227. client_sock = INVALID_SOCKET;
  228. }
  229. fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT);
  230. fflush(stderr);
  231. client_sock = accept(server_sock, NULL, NULL);
  232. if (client_sock == INVALID_SOCKET) {
  233. fprintf(stderr, "%s: accept() failed with error %ld, skip waiting for connection\n",
  234. __FILE__, (long) WSAGetLastError());
  235. fflush(stderr);
  236. goto fail;
  237. }
  238. fprintf(stderr, "Debug connection established\n");
  239. fflush(stderr);
  240. /* XXX: For now, close the listen socket because we won't accept new
  241. * connections anyway. A better implementation would allow multiple
  242. * debug attaches.
  243. */
  244. if (server_sock != INVALID_SOCKET) {
  245. (void) closesocket(server_sock);
  246. server_sock = INVALID_SOCKET;
  247. }
  248. return;
  249. fail:
  250. if (client_sock != INVALID_SOCKET) {
  251. (void) closesocket(client_sock);
  252. client_sock = INVALID_SOCKET;
  253. }
  254. }
  255. /*
  256. * Duktape callbacks
  257. */
  258. /* Duktape debug transport callback: (possibly partial) read. */
  259. duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) {
  260. int ret;
  261. (void) udata; /* not needed by the example */
  262. #if defined(DEBUG_PRINTS)
  263. fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
  264. __FUNCTION__, (void *) udata, (void *) buffer, (long) length);
  265. fflush(stderr);
  266. #endif
  267. if (client_sock == INVALID_SOCKET) {
  268. return 0;
  269. }
  270. if (length == 0) {
  271. /* This shouldn't happen. */
  272. fprintf(stderr, "%s: read request length == 0, closing connection\n",
  273. __FILE__);
  274. fflush(stderr);
  275. goto fail;
  276. }
  277. if (buffer == NULL) {
  278. /* This shouldn't happen. */
  279. fprintf(stderr, "%s: read request buffer == NULL, closing connection\n",
  280. __FILE__);
  281. fflush(stderr);
  282. goto fail;
  283. }
  284. /* In a production quality implementation there would be a sanity
  285. * timeout here to recover from "black hole" disconnects.
  286. */
  287. ret = recv(client_sock, buffer, (int) length, 0);
  288. if (ret < 0) {
  289. fprintf(stderr, "%s: debug read failed, error %d, closing connection\n",
  290. __FILE__, ret);
  291. fflush(stderr);
  292. goto fail;
  293. } else if (ret == 0) {
  294. fprintf(stderr, "%s: debug read failed, ret == 0 (EOF), closing connection\n",
  295. __FILE__);
  296. fflush(stderr);
  297. goto fail;
  298. } else if (ret > (int) length) {
  299. fprintf(stderr, "%s: debug read failed, ret too large (%ld > %ld), closing connection\n",
  300. __FILE__, (long) ret, (long) length);
  301. fflush(stderr);
  302. goto fail;
  303. }
  304. return (duk_size_t) ret;
  305. fail:
  306. if (client_sock != INVALID_SOCKET) {
  307. (void) closesocket(client_sock);
  308. client_sock = INVALID_SOCKET;
  309. }
  310. return 0;
  311. }
  312. /* Duktape debug transport callback: (possibly partial) write. */
  313. duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) {
  314. int ret;
  315. (void) udata; /* not needed by the example */
  316. #if defined(DEBUG_PRINTS)
  317. fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
  318. __FUNCTION__, (void *) udata, (const void *) buffer, (long) length);
  319. fflush(stderr);
  320. #endif
  321. if (client_sock == INVALID_SOCKET) {
  322. return 0;
  323. }
  324. if (length == 0) {
  325. /* This shouldn't happen. */
  326. fprintf(stderr, "%s: write request length == 0, closing connection\n",
  327. __FILE__);
  328. fflush(stderr);
  329. goto fail;
  330. }
  331. if (buffer == NULL) {
  332. /* This shouldn't happen. */
  333. fprintf(stderr, "%s: write request buffer == NULL, closing connection\n",
  334. __FILE__);
  335. fflush(stderr);
  336. goto fail;
  337. }
  338. /* In a production quality implementation there would be a sanity
  339. * timeout here to recover from "black hole" disconnects.
  340. */
  341. ret = send(client_sock, buffer, (int) length, 0);
  342. if (ret <= 0 || ret > (int) length) {
  343. fprintf(stderr, "%s: debug write failed, ret %d, closing connection\n",
  344. __FILE__, ret);
  345. fflush(stderr);
  346. goto fail;
  347. }
  348. return (duk_size_t) ret;
  349. fail:
  350. if (client_sock != INVALID_SOCKET) {
  351. (void) closesocket(INVALID_SOCKET);
  352. client_sock = INVALID_SOCKET;
  353. }
  354. return 0;
  355. }
  356. duk_size_t duk_trans_socket_peek_cb(void *udata) {
  357. u_long avail;
  358. int rc;
  359. (void) udata; /* not needed by the example */
  360. #if defined(DEBUG_PRINTS)
  361. fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
  362. fflush(stderr);
  363. #endif
  364. if (client_sock == INVALID_SOCKET) {
  365. return 0;
  366. }
  367. avail = 0;
  368. rc = ioctlsocket(client_sock, FIONREAD, &avail);
  369. if (rc != 0) {
  370. fprintf(stderr, "%s: ioctlsocket() returned %d, closing connection\n",
  371. __FILE__, rc);
  372. fflush(stderr);
  373. goto fail; /* also returns 0, which is correct */
  374. } else {
  375. if (avail == 0) {
  376. return 0; /* nothing to read */
  377. } else {
  378. return 1; /* something to read */
  379. }
  380. }
  381. /* never here */
  382. fail:
  383. if (client_sock != INVALID_SOCKET) {
  384. (void) closesocket(client_sock);
  385. client_sock = INVALID_SOCKET;
  386. }
  387. return 0;
  388. }
  389. void duk_trans_socket_read_flush_cb(void *udata) {
  390. (void) udata; /* not needed by the example */
  391. #if defined(DEBUG_PRINTS)
  392. fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
  393. fflush(stderr);
  394. #endif
  395. /* Read flush: Duktape may not be making any more read calls at this
  396. * time. If the transport maintains a receive window, it can use a
  397. * read flush as a signal to update the window status to the remote
  398. * peer. A read flush is guaranteed to occur before Duktape stops
  399. * reading for a while; it may occur in other situations as well so
  400. * it's not a 100% reliable indication.
  401. */
  402. /* This TCP transport requires no read flush handling so ignore.
  403. * You can also pass a NULL to duk_debugger_attach() and not
  404. * implement this callback at all.
  405. */
  406. }
  407. void duk_trans_socket_write_flush_cb(void *udata) {
  408. (void) udata; /* not needed by the example */
  409. #if defined(DEBUG_PRINTS)
  410. fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
  411. fflush(stderr);
  412. #endif
  413. /* Write flush. If the transport combines multiple writes
  414. * before actually sending, a write flush is an indication
  415. * to write out any pending bytes: Duktape may not be doing
  416. * any more writes on this occasion.
  417. */
  418. /* This TCP transport requires no write flush handling so ignore.
  419. * You can also pass a NULL to duk_debugger_attach() and not
  420. * implement this callback at all.
  421. */
  422. return;
  423. }
  424. #undef DUK__STRINGIFY_HELPER
  425. #undef DUK__STRINGIFY
  426. #else // OSX ad LINUX
  427. /*
  428. * Example debug transport using a Linux/Unix TCP socket
  429. *
  430. * Provides a TCP server socket which a debug client can connect to.
  431. * After that data is just passed through.
  432. */
  433. #include <stdio.h>
  434. #include <string.h>
  435. #include <sys/socket.h>
  436. #include <netinet/in.h>
  437. #include <unistd.h>
  438. #include <poll.h>
  439. #include <errno.h>
  440. #include <ThirdParty/Duktape/duktape.h>
  441. #if !defined(DUK_DEBUG_PORT)
  442. #define DUK_DEBUG_PORT 9091
  443. #endif
  444. #if 0
  445. #define DEBUG_PRINTS
  446. #endif
  447. static int server_sock = -1;
  448. static int client_sock = -1;
  449. /*
  450. * Transport init and finish
  451. */
  452. void duk_trans_socket_init(void) {
  453. struct sockaddr_in addr;
  454. int on;
  455. server_sock = socket(AF_INET, SOCK_STREAM, 0);
  456. if (server_sock < 0) {
  457. fprintf(stderr, "%s: failed to create server socket: %s\n",
  458. __FILE__, strerror(errno));
  459. fflush(stderr);
  460. goto fail;
  461. }
  462. on = 1;
  463. if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) < 0) {
  464. fprintf(stderr, "%s: failed to set SO_REUSEADDR for server socket: %s\n",
  465. __FILE__, strerror(errno));
  466. fflush(stderr);
  467. goto fail;
  468. }
  469. memset((void *) &addr, 0, sizeof(addr));
  470. addr.sin_family = AF_INET;
  471. addr.sin_addr.s_addr = INADDR_ANY;
  472. addr.sin_port = htons(DUK_DEBUG_PORT);
  473. if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  474. fprintf(stderr, "%s: failed to bind server socket: %s\n",
  475. __FILE__, strerror(errno));
  476. fflush(stderr);
  477. goto fail;
  478. }
  479. listen(server_sock, 1 /*backlog*/);
  480. return;
  481. fail:
  482. if (server_sock >= 0) {
  483. (void) close(server_sock);
  484. server_sock = -1;
  485. }
  486. }
  487. void duk_trans_socket_waitconn(void) {
  488. struct sockaddr_in addr;
  489. socklen_t sz;
  490. if (server_sock < 0) {
  491. fprintf(stderr, "%s: no server socket, skip waiting for connection\n",
  492. __FILE__);
  493. fflush(stderr);
  494. return;
  495. }
  496. if (client_sock >= 0) {
  497. (void) close(client_sock);
  498. client_sock = -1;
  499. }
  500. fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT);
  501. fflush(stderr);
  502. sz = (socklen_t) sizeof(addr);
  503. client_sock = accept(server_sock, (struct sockaddr *) &addr, &sz);
  504. if (client_sock < 0) {
  505. fprintf(stderr, "%s: accept() failed, skip waiting for connection: %s\n",
  506. __FILE__, strerror(errno));
  507. fflush(stderr);
  508. goto fail;
  509. }
  510. fprintf(stderr, "Debug connection established\n");
  511. fflush(stderr);
  512. /* XXX: For now, close the listen socket because we won't accept new
  513. * connections anyway. A better implementation would allow multiple
  514. * debug attaches.
  515. */
  516. if (server_sock >= 0) {
  517. (void) close(server_sock);
  518. server_sock = -1;
  519. }
  520. return;
  521. fail:
  522. if (client_sock >= 0) {
  523. (void) close(client_sock);
  524. client_sock = -1;
  525. }
  526. }
  527. void duk_trans_socket_finish(void) {
  528. if (client_sock >= 0) {
  529. (void) close(client_sock);
  530. client_sock = -1;
  531. }
  532. if (server_sock >= 0) {
  533. (void) close(server_sock);
  534. server_sock = -1;
  535. }
  536. // try to auto-restart
  537. do_reconnect();
  538. }
  539. /*
  540. * Duktape callbacks
  541. */
  542. /* Duktape debug transport callback: (possibly partial) read. */
  543. duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) {
  544. ssize_t ret;
  545. (void) udata; /* not needed by the example */
  546. #if defined(DEBUG_PRINTS)
  547. fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
  548. __func__, (void *) udata, (void *) buffer, (long) length);
  549. fflush(stderr);
  550. #endif
  551. if (client_sock < 0) {
  552. return 0;
  553. }
  554. if (length == 0) {
  555. /* This shouldn't happen. */
  556. fprintf(stderr, "%s: read request length == 0, closing connection\n",
  557. __FILE__);
  558. fflush(stderr);
  559. goto fail;
  560. }
  561. if (buffer == NULL) {
  562. /* This shouldn't happen. */
  563. fprintf(stderr, "%s: read request buffer == NULL, closing connection\n",
  564. __FILE__);
  565. fflush(stderr);
  566. goto fail;
  567. }
  568. /* In a production quality implementation there would be a sanity
  569. * timeout here to recover from "black hole" disconnects.
  570. */
  571. ret = read(client_sock, (void *) buffer, (size_t) length);
  572. if (ret < 0) {
  573. fprintf(stderr, "%s: debug read failed, closing connection: %s\n",
  574. __FILE__, strerror(errno));
  575. fflush(stderr);
  576. goto fail;
  577. } else if (ret == 0) {
  578. fprintf(stderr, "%s: debug read failed, ret == 0 (EOF), closing connection\n",
  579. __FILE__);
  580. fflush(stderr);
  581. goto fail;
  582. } else if (ret > (ssize_t) length) {
  583. fprintf(stderr, "%s: debug read failed, ret too large (%ld > %ld), closing connection\n",
  584. __FILE__, (long) ret, (long) length);
  585. fflush(stderr);
  586. goto fail;
  587. }
  588. return (duk_size_t) ret;
  589. fail:
  590. if (client_sock >= 0) {
  591. (void) close(client_sock);
  592. client_sock = -1;
  593. }
  594. return 0;
  595. }
  596. /* Duktape debug transport callback: (possibly partial) write. */
  597. duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) {
  598. ssize_t ret;
  599. (void) udata; /* not needed by the example */
  600. #if defined(DEBUG_PRINTS)
  601. fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
  602. __func__, (void *) udata, (const void *) buffer, (long) length);
  603. fflush(stderr);
  604. #endif
  605. if (client_sock < 0) {
  606. return 0;
  607. }
  608. if (length == 0) {
  609. /* This shouldn't happen. */
  610. fprintf(stderr, "%s: write request length == 0, closing connection\n",
  611. __FILE__);
  612. fflush(stderr);
  613. goto fail;
  614. }
  615. if (buffer == NULL) {
  616. /* This shouldn't happen. */
  617. fprintf(stderr, "%s: write request buffer == NULL, closing connection\n",
  618. __FILE__);
  619. fflush(stderr);
  620. goto fail;
  621. }
  622. /* In a production quality implementation there would be a sanity
  623. * timeout here to recover from "black hole" disconnects.
  624. */
  625. ret = write(client_sock, (const void *) buffer, (size_t) length);
  626. if (ret <= 0 || ret > (ssize_t) length) {
  627. fprintf(stderr, "%s: debug write failed, closing connection: %s\n",
  628. __FILE__, strerror(errno));
  629. fflush(stderr);
  630. goto fail;
  631. }
  632. return (duk_size_t) ret;
  633. fail:
  634. if (client_sock >= 0) {
  635. (void) close(client_sock);
  636. client_sock = -1;
  637. }
  638. return 0;
  639. }
  640. duk_size_t duk_trans_socket_peek_cb(void *udata) {
  641. struct pollfd fds[1];
  642. int poll_rc;
  643. (void) udata; /* not needed by the example */
  644. #if defined(DEBUG_PRINTS)
  645. fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
  646. fflush(stderr);
  647. #endif
  648. if (client_sock < 0) {
  649. return 0;
  650. }
  651. fds[0].fd = client_sock;
  652. fds[0].events = POLLIN;
  653. fds[0].revents = 0;
  654. poll_rc = poll(fds, 1, 0);
  655. if (poll_rc < 0) {
  656. fprintf(stderr, "%s: poll returned < 0, closing connection: %s\n",
  657. __FILE__, strerror(errno));
  658. fflush(stderr);
  659. goto fail; /* also returns 0, which is correct */
  660. } else if (poll_rc > 1) {
  661. fprintf(stderr, "%s: poll returned > 1, treating like 1\n",
  662. __FILE__);
  663. fflush(stderr);
  664. return 1; /* should never happen */
  665. } else if (poll_rc == 0) {
  666. return 0; /* nothing to read */
  667. } else {
  668. return 1; /* something to read */
  669. }
  670. fail:
  671. if (client_sock >= 0) {
  672. (void) close(client_sock);
  673. client_sock = -1;
  674. }
  675. return 0;
  676. }
  677. void duk_trans_socket_read_flush_cb(void *udata) {
  678. (void) udata; /* not needed by the example */
  679. #if defined(DEBUG_PRINTS)
  680. fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
  681. fflush(stderr);
  682. #endif
  683. /* Read flush: Duktape may not be making any more read calls at this
  684. * time. If the transport maintains a receive window, it can use a
  685. * read flush as a signal to update the window status to the remote
  686. * peer. A read flush is guaranteed to occur before Duktape stops
  687. * reading for a while; it may occur in other situations as well so
  688. * it's not a 100% reliable indication.
  689. */
  690. /* This TCP transport requires no read flush handling so ignore.
  691. * You can also pass a NULL to duk_debugger_attach() and not
  692. * implement this callback at all.
  693. */
  694. }
  695. void duk_trans_socket_write_flush_cb(void *udata) {
  696. (void) udata; /* not needed by the example */
  697. #if defined(DEBUG_PRINTS)
  698. fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
  699. fflush(stderr);
  700. #endif
  701. /* Write flush. If the transport combines multiple writes
  702. * before actually sending, a write flush is an indication
  703. * to write out any pending bytes: Duktape may not be doing
  704. * any more writes on this occasion.
  705. */
  706. /* This TCP transport requires no write flush handling so ignore.
  707. * You can also pass a NULL to duk_debugger_attach() and not
  708. * implement this callback at all.
  709. */
  710. return;
  711. }
  712. #endif // OSX ad LINUX
  713. void debugger_detached (duk_context* ctx, void *udata) {
  714. fflush(stderr);
  715. /* Ensure socket is closed even when detach is initiated by Duktape
  716. * rather than debug client.
  717. */
  718. duk_trans_socket_finish();
  719. }