JSDebugger.cpp 23 KB

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