1 | /* Copyright JS Foundation and other contributors, http://js.foundation |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. |
5 | * You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | * See the License for the specific language governing permissions and |
13 | * limitations under the License. |
14 | */ |
15 | |
16 | #include "debugger.h" |
17 | #include "jcontext.h" |
18 | #include "jerryscript.h" |
19 | |
20 | #if ENABLED (JERRY_DEBUGGER) |
21 | |
22 | /** |
23 | * Minimum number of bytes transmitted or received. |
24 | */ |
25 | #define JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE 64 |
26 | |
27 | /** |
28 | * Sleep time in milliseconds between each jerry_debugger_receive call |
29 | */ |
30 | #define JERRY_DEBUGGER_TRANSPORT_TIMEOUT 100 |
31 | |
32 | /** |
33 | * Add a new transport layer. |
34 | */ |
35 | void |
36 | jerry_debugger_transport_add (jerry_debugger_transport_header_t *header_p, /**< transport implementation */ |
37 | size_t send_message_header_size, /**< header bytes reserved for outgoing messages */ |
38 | size_t max_send_message_size, /**< maximum number of bytes transmitted in a message */ |
39 | size_t receive_message_header_size, /**< header bytes reserved for incoming messages */ |
40 | size_t max_receive_message_size) /**< maximum number of bytes received in a message */ |
41 | { |
42 | JERRY_ASSERT (max_send_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE |
43 | && max_receive_message_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE); |
44 | |
45 | header_p->next_p = JERRY_CONTEXT (debugger_transport_header_p); |
46 | JERRY_CONTEXT (debugger_transport_header_p) = header_p; |
47 | |
48 | uint8_t *payload_p; |
49 | size_t max_send_size; |
50 | size_t max_receive_size; |
51 | |
52 | if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) |
53 | { |
54 | payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p); |
55 | max_send_size = JERRY_CONTEXT (debugger_max_send_size); |
56 | max_receive_size = JERRY_CONTEXT (debugger_max_receive_size); |
57 | } |
58 | else |
59 | { |
60 | JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_CONNECTED); |
61 | payload_p = JERRY_CONTEXT (debugger_send_buffer); |
62 | max_send_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE; |
63 | max_receive_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE; |
64 | } |
65 | |
66 | JERRY_ASSERT (max_send_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + send_message_header_size); |
67 | JERRY_ASSERT (max_receive_size > JERRY_DEBUGGER_TRANSPORT_MIN_BUFFER_SIZE + receive_message_header_size); |
68 | |
69 | JERRY_CONTEXT (debugger_send_buffer_payload_p) = payload_p + send_message_header_size; |
70 | |
71 | max_send_size = max_send_size - send_message_header_size; |
72 | max_receive_size = max_receive_size - receive_message_header_size; |
73 | |
74 | if (max_send_size > max_send_message_size) |
75 | { |
76 | max_send_size = max_send_message_size; |
77 | } |
78 | |
79 | if (max_receive_size > max_receive_message_size) |
80 | { |
81 | max_receive_size = max_receive_message_size; |
82 | } |
83 | |
84 | JERRY_CONTEXT (debugger_max_send_size) = (uint8_t) max_send_size; |
85 | JERRY_CONTEXT (debugger_max_receive_size) = (uint8_t) max_receive_size; |
86 | } /* jerry_debugger_transport_add */ |
87 | |
88 | /** |
89 | * Starts the communication to the debugger client. |
90 | * Must be called after the connection is successfully established. |
91 | */ |
92 | void |
93 | jerry_debugger_transport_start (void) |
94 | { |
95 | JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); |
96 | |
97 | if (jerry_debugger_send_configuration (JERRY_CONTEXT (debugger_max_receive_size))) |
98 | { |
99 | JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP); |
100 | JERRY_CONTEXT (debugger_stop_context) = NULL; |
101 | } |
102 | } /* jerry_debugger_transport_start */ |
103 | |
104 | /** |
105 | * Returns true if a debugger client is connected. |
106 | * |
107 | * @return true - a debugger client is connected, |
108 | * false - otherwise |
109 | */ |
110 | bool |
111 | jerry_debugger_transport_is_connected (void) |
112 | { |
113 | return (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) != 0; |
114 | } /* jerry_debugger_transport_is_connected */ |
115 | |
116 | /** |
117 | * Notifies the debugger server that the connection is closed. |
118 | */ |
119 | void |
120 | jerry_debugger_transport_close (void) |
121 | { |
122 | if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)) |
123 | { |
124 | return; |
125 | } |
126 | |
127 | JERRY_CONTEXT (debugger_flags) = JERRY_DEBUGGER_VM_IGNORE; |
128 | |
129 | jerry_debugger_transport_header_t *current_p = JERRY_CONTEXT (debugger_transport_header_p); |
130 | |
131 | JERRY_ASSERT (current_p != NULL); |
132 | |
133 | do |
134 | { |
135 | jerry_debugger_transport_header_t *next_p = current_p->next_p; |
136 | |
137 | current_p->close (current_p); |
138 | |
139 | current_p = next_p; |
140 | } |
141 | while (current_p != NULL); |
142 | |
143 | jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "Debugger client connection closed.\n" ); |
144 | |
145 | jerry_debugger_free_unreferenced_byte_code (); |
146 | } /* jerry_debugger_transport_close */ |
147 | |
148 | /** |
149 | * Send data over the current connection |
150 | * |
151 | * @return true - data sent successfully, |
152 | * false - connection closed |
153 | */ |
154 | bool |
155 | jerry_debugger_transport_send (const uint8_t *message_p, /**< message to be sent */ |
156 | size_t message_length) /**< message length in bytes */ |
157 | { |
158 | JERRY_ASSERT (jerry_debugger_transport_is_connected ()); |
159 | JERRY_ASSERT (message_length > 0); |
160 | |
161 | jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p); |
162 | uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer_payload_p); |
163 | size_t max_send_size = JERRY_CONTEXT (debugger_max_send_size); |
164 | |
165 | do |
166 | { |
167 | size_t fragment_length = (message_length <= max_send_size ? message_length |
168 | : max_send_size); |
169 | |
170 | memcpy (payload_p, message_p, fragment_length); |
171 | |
172 | if (!header_p->send (header_p, payload_p, fragment_length)) |
173 | { |
174 | return false; |
175 | } |
176 | |
177 | message_p += fragment_length; |
178 | message_length -= fragment_length; |
179 | } |
180 | while (message_length > 0); |
181 | |
182 | return true; |
183 | } /* jerry_debugger_transport_send */ |
184 | |
185 | /** |
186 | * Receive data from the current connection |
187 | * |
188 | * Note: |
189 | * A message is received if message_start_p is not NULL |
190 | * |
191 | * @return true - function successfully completed, |
192 | * false - connection closed |
193 | */ |
194 | bool |
195 | jerry_debugger_transport_receive (jerry_debugger_transport_receive_context_t *context_p) /**< [out] receive |
196 | * context */ |
197 | { |
198 | JERRY_ASSERT (jerry_debugger_transport_is_connected ()); |
199 | |
200 | context_p->buffer_p = JERRY_CONTEXT (debugger_receive_buffer); |
201 | context_p->received_length = JERRY_CONTEXT (debugger_received_length); |
202 | context_p->message_p = NULL; |
203 | context_p->message_length = 0; |
204 | context_p->message_total_length = 0; |
205 | |
206 | jerry_debugger_transport_header_t *header_p = JERRY_CONTEXT (debugger_transport_header_p); |
207 | |
208 | return header_p->receive (header_p, context_p); |
209 | } /* jerry_debugger_transport_receive */ |
210 | |
211 | /** |
212 | * Clear the message buffer after the message is processed |
213 | */ |
214 | void |
215 | jerry_debugger_transport_receive_completed (jerry_debugger_transport_receive_context_t *context_p) /**< receive |
216 | * context */ |
217 | { |
218 | JERRY_ASSERT (context_p->message_p != NULL); |
219 | JERRY_ASSERT (context_p->buffer_p == JERRY_CONTEXT (debugger_receive_buffer)); |
220 | |
221 | size_t message_total_length = context_p->message_total_length; |
222 | size_t received_length = context_p->received_length; |
223 | |
224 | JERRY_ASSERT (message_total_length <= received_length); |
225 | |
226 | if (message_total_length == 0 || message_total_length == received_length) |
227 | { |
228 | /* All received data is processed. */ |
229 | JERRY_CONTEXT (debugger_received_length) = 0; |
230 | return; |
231 | } |
232 | |
233 | uint8_t *buffer_p = context_p->buffer_p; |
234 | received_length -= message_total_length; |
235 | |
236 | memmove (buffer_p, buffer_p + message_total_length, received_length); |
237 | |
238 | JERRY_CONTEXT (debugger_received_length) = (uint16_t) received_length; |
239 | } /* jerry_debugger_transport_receive_completed */ |
240 | |
241 | /** |
242 | * Suspend execution for a predefined time (JERRY_DEBUGGER_TRANSPORT_TIMEOUT ms). |
243 | */ |
244 | void |
245 | jerry_debugger_transport_sleep (void) |
246 | { |
247 | jerry_port_sleep (JERRY_DEBUGGER_TRANSPORT_TIMEOUT); |
248 | } /* jerry_debugger_transport_sleep */ |
249 | |
250 | #endif /* ENABLED (JERRY_DEBUGGER) */ |
251 | |