JSDebugger.cpp 23 KB

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