JSDebugger.cpp 23 KB

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