1 /*
2 * Copyright 2006-2008 The FLWOR Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "stdafx.h"
17 #include <iostream>
18 #include <sstream>
19 #include <cassert>
20 #include <zorba/debugger_event_handler.h>
21 #include <zorba/base64.h>
22 #include "debugger/debugger_clientimpl.h"
23 #include "debugger/socket_streambuf.h"
24
25 namespace zorba {
26
27 DebuggerClient*
createDebuggerClient(DebuggerEventHandler * aHandler,unsigned short aPort,const std::string & aHost)28 DebuggerClient::createDebuggerClient(
29 DebuggerEventHandler* aHandler,
30 unsigned short aPort,
31 const std::string& aHost)
32 {
33 return new DebuggerClientImpl(aHost, aPort, aHandler);
34 }
35
36 // ****************************************************************************
37
DebuggerListener(DebuggerClientImpl * aClient)38 DebuggerListener::DebuggerListener(DebuggerClientImpl* aClient)
39 : theClient(aClient), theStopLooping(false)
40 {
41 }
42
43 void
run()44 DebuggerListener::run()
45 {
46 while (!theStopLooping) {
47 std::string str;
48 std::getline(*(theClient->theInStream), str, '\0');
49
50 #ifndef NDEBUG
51 std::stringstream lStr(str);
52 std::size_t length;
53 lStr >> length;
54 #endif
55
56 std::getline(*(theClient->theInStream), str, '\0');
57
58 // only assert if we have a good stream (no stream was broken,
59 // case in which an empty string will be returned)
60 if (theClient->theInStream->good()) {
61 #ifndef NDEBUG
62 assert(str.size() == length);
63 #endif
64 } else {
65 break;
66 }
67
68 theClient->theHandler->parseMessage(str);
69
70 // TODO: this was the initial implementation. This will have to change
71 this->sleep_(250);
72 }
73 }
74
75 void
finish()76 DebuggerListener::finish()
77 {
78 }
79
80 void
stopLooping()81 DebuggerListener::stopLooping()
82 {
83 theStopLooping = true;
84 }
85
86 // ****************************************************************************
87
~DebuggerClient()88 DebuggerClient::~DebuggerClient()
89 {
90 }
91
DebuggerClientImpl(const std::string & aHost,unsigned short aPort,DebuggerEventHandler * aHandler)92 DebuggerClientImpl::DebuggerClientImpl(
93 const std::string& aHost,
94 unsigned short aPort,
95 DebuggerEventHandler* aHandler)
96 : theServerSocket(aHost, aPort),
97 theSocket(0),
98 theInStreamBuffer(0),
99 theOutStreamBuffer(0),
100 theInStream(0),
101 theOutStream(0),
102 theHandler(aHandler),
103 theListener(0),
104 theLastId(0)
105 {
106 }
107
~DebuggerClientImpl()108 DebuggerClientImpl::~DebuggerClientImpl()
109 {
110 if (theListener) {
111 if (theListener->status() == DebuggerListener::RUNNING) {
112 theListener->stopLooping();
113 theListener->join();
114 }
115 delete theListener;
116 }
117 if (theSocket) {
118 theInStream->sync();
119 delete theInStream;
120
121 theOutStream->flush();
122 delete theInStream;
123
124 delete theOutStreamBuffer;
125 delete theInStreamBuffer;
126
127 theSocket->close();
128 theSocket->cleanUp();
129 delete theSocket;
130 }
131 }
132
133 void
accept()134 DebuggerClientImpl::accept()
135 {
136 theListener = new DebuggerListener(this);
137 theSocket = theServerSocket.accept();
138 theInStreamBuffer = new SocketStreambuf(*theSocket);
139 theOutStreamBuffer = new SocketStreambuf(*theSocket);
140 theInStream = new std::istream(theInStreamBuffer);
141 theOutStream = new std::ostream(theOutStreamBuffer);
142 theListener->start();
143 }
144
145 std::size_t
status()146 DebuggerClientImpl::status()
147 {
148 std::size_t id = ++theLastId;
149 *theOutStream << "status -i " << id << '\0';
150 theOutStream->flush();
151 return id;
152 }
153
154 std::size_t
variables()155 DebuggerClientImpl::variables()
156 {
157 // we hack the protocol to return all properties if the context ID is -1
158 std::size_t id = ++theLastId;
159 *theOutStream << "context_get -c -1 -i " << id << '\0';
160 theOutStream->flush();
161 return id;
162 }
163
164 std::size_t
feature_get(std::string const & aFeatureName)165 DebuggerClientImpl::feature_get(std::string const& aFeatureName)
166 {
167 std::size_t id = ++theLastId;
168 *theOutStream << "feature_get -i " << id << " -n " << aFeatureName << '\0';
169 theOutStream->flush();
170 return id;
171 }
172
173 std::size_t
feature_set(std::string const & aFeatureName,std::string const & aValue)174 DebuggerClientImpl::feature_set(std::string const &aFeatureName,
175 std::string const &aValue)
176 {
177 std::size_t id = ++theLastId;
178 *theOutStream << "feature_set -i " << id << " -n " << aFeatureName << " -v "
179 << aValue << '\0';
180 theOutStream->flush();
181 return id;
182 }
183
184 std::size_t
run()185 DebuggerClientImpl::run()
186 {
187 std::size_t id = ++theLastId;
188 *theOutStream << "run -i " << id << '\0';
189 theOutStream->flush();
190 return id;
191 }
192
193 std::size_t
step_into()194 DebuggerClientImpl::step_into()
195 {
196 std::size_t id = ++theLastId;
197 *theOutStream << "step_into -i " << id << '\0';
198 theOutStream->flush();
199 return id;
200 }
201
202 std::size_t
step_over()203 DebuggerClientImpl::step_over()
204 {
205 std::size_t id = ++theLastId;
206 *theOutStream << "step_over -i " << id << '\0';
207 theOutStream->flush();
208 return id;
209 }
210
211 std::size_t
step_out()212 DebuggerClientImpl::step_out()
213 {
214 std::size_t id = ++theLastId;
215 *theOutStream << "step_out -i " << id << '\0';
216 theOutStream->flush();
217 return id;
218 }
219
220 std::size_t
stop(bool withQuit)221 DebuggerClientImpl::stop(bool withQuit)
222 {
223 std::size_t id = ++theLastId;
224 *theOutStream << "stop -i " << id;
225 if (!withQuit) {
226 *theOutStream << " -z 1";
227 }
228 *theOutStream << '\0';
229 theOutStream->flush();
230 return id;
231 }
232
233 std::size_t
detach()234 DebuggerClientImpl::detach()
235 {
236 std::size_t id = ++theLastId;
237 *theOutStream << "detach -i " << id << '\0';
238 theOutStream->flush();
239 return id;
240 }
241
242 std::size_t
breakpoint_set(BreakpointType aType,bool aEnabled,const std::string & aFilename,int aLinenumber,const std::string & aFunctionName,const std::string & aExceptionName,unsigned hit_value,HitCondition aCondition,bool aIsTemporary,const std::string & aExpression)243 DebuggerClientImpl::breakpoint_set(
244 BreakpointType aType,
245 bool aEnabled,
246 const std::string& aFilename,
247 int aLinenumber,
248 const std::string& aFunctionName,
249 const std::string& aExceptionName,
250 unsigned hit_value,
251 HitCondition aCondition,
252 bool aIsTemporary,
253 const std::string& aExpression)
254 {
255 std::size_t id = ++theLastId;
256 *theOutStream << "breakpoint_set -i " << id
257 << " -t ";
258 switch (aType) {
259 case Line:
260 *theOutStream << "line";
261 break;
262 case Call:
263 *theOutStream << "call";
264 break;
265 case Return:
266 *theOutStream << "return";
267 break;
268 case Exception:
269 *theOutStream << "exception";
270 break;
271 case Conditional:
272 *theOutStream << "conditional";
273 break;
274 case Watch:
275 *theOutStream << "watch";
276 break;
277 }
278 if (!aEnabled)
279 *theOutStream << " -s disabled";
280 if (aFilename != "") {
281 *theOutStream << " -f \"" << aFilename << "\"";
282 }
283 if (aLinenumber != -1)
284 *theOutStream << " -n " << aLinenumber;
285 if (aFunctionName != "")
286 *theOutStream << " -m " << aFunctionName;
287 if (aExceptionName != "")
288 *theOutStream << " -x " << aExceptionName;
289 if (hit_value != 0)
290 *theOutStream << " -h " << hit_value;
291 switch (aCondition) {
292 case BiggerEqual:
293 break;
294 case Equal:
295 *theOutStream << " -o == ";
296 break;
297 case Multiple:
298 *theOutStream << " -o % ";
299 }
300 if (aIsTemporary)
301 *theOutStream << " -r 1 ";
302 if (aExpression != "")
303 *theOutStream << " -- " << aExpression;
304 *theOutStream << '\0';
305 theOutStream->flush();
306 return id;
307 }
308
309 std::size_t
breakpoint_get(std::size_t aBreakpointId)310 DebuggerClientImpl::breakpoint_get(std::size_t aBreakpointId)
311 {
312 std::size_t id = ++theLastId;
313 *theOutStream << "breakpoint_get -i " << id << " -d " << aBreakpointId << '\0';
314 theOutStream->flush();
315 return id;
316 }
317
318 std::size_t
breakpoint_update(std::size_t aBreakpointId,bool aEnabled,int aLinenumber,unsigned hit_value,HitCondition aCondition)319 DebuggerClientImpl::breakpoint_update(
320 std::size_t aBreakpointId,
321 bool aEnabled,
322 int aLinenumber,
323 unsigned hit_value,
324 HitCondition aCondition)
325 {
326 std::size_t id = ++theLastId;
327 *theOutStream << "breakpoint_update -i " << id
328 << " -d " << aBreakpointId;
329 if (aEnabled)
330 *theOutStream << " -s disabled";
331 if (aLinenumber != -1)
332 *theOutStream << " -n " << aLinenumber;
333 if (hit_value != 0)
334 *theOutStream << " -h " << hit_value;
335 switch (aCondition) {
336 case BiggerEqual:
337 break;
338 case Equal:
339 *theOutStream << " -o == ";
340 break;
341 case Multiple:
342 *theOutStream << " -o % ";
343 }
344 *theOutStream << '\0';
345 theOutStream->flush();
346 return id;
347 }
348
349 std::size_t
breakpoint_remove(std::size_t aBreakpointId)350 DebuggerClientImpl::breakpoint_remove(std::size_t aBreakpointId)
351 {
352 std::size_t id = ++theLastId;
353 *theOutStream << "breakpoint_remove -i " << id << " -d " << aBreakpointId << '\0';
354 theOutStream->flush();
355 return id;
356 }
357
358 std::size_t
breakpoint_list()359 DebuggerClientImpl::breakpoint_list()
360 {
361 std::size_t id = ++theLastId;
362 *theOutStream << "breakpoint_list -i " << id << '\0';
363 theOutStream->flush();
364 return id;
365 }
366
367 std::size_t
stack_depth()368 DebuggerClientImpl::stack_depth()
369 {
370 std::size_t id = ++theLastId;
371 *theOutStream << "stack_depth -i " << id << '\0';
372 theOutStream->flush();
373 return id;
374 }
375
376 std::size_t
stack_get(int depth)377 DebuggerClientImpl::stack_get(int depth)
378 {
379 std::size_t id = ++theLastId;
380 *theOutStream << "stack_get";
381 if (depth >= 0) {
382 *theOutStream << " -d " << depth;
383 }
384 *theOutStream << " -i " << id << '\0';
385 theOutStream->flush();
386 return id;
387 }
388
389 std::size_t
context_names(int depth)390 DebuggerClientImpl::context_names(int depth)
391 {
392 std::size_t id = ++theLastId;
393 *theOutStream << "context_names";
394 if (depth >= 0) {
395 *theOutStream << " -d " << depth;
396 }
397 *theOutStream << " -i " << id << '\0';
398 theOutStream->flush();
399 return id;
400 }
401
402 std::size_t
context_get(int depth,int contextId)403 DebuggerClientImpl::context_get(int depth, int contextId)
404 {
405 std::size_t id = ++theLastId;
406 *theOutStream << "context_get";
407 if (depth >= 0) {
408 *theOutStream << " -d " << depth;
409 }
410 if (contextId >= 0){
411 *theOutStream << " -c " << contextId;
412 }
413 *theOutStream << " -i " << id << '\0';
414 theOutStream->flush();
415 return id;
416 }
417
418 std::size_t
typemap_get()419 DebuggerClientImpl::typemap_get()
420 {
421 std::size_t id = ++theLastId;
422 *theOutStream << "typemap_get -i " << id << '\0';
423 theOutStream->flush();
424 return id;
425 }
426
427 std::size_t
property_get(const std::string & aPropertyLongName,int aStackDepth,int aContextId,std::size_t aMaxDataSize,int aDatapage,const std::string & aPropertyKey)428 DebuggerClientImpl::property_get(
429 const std::string& aPropertyLongName,
430 int aStackDepth,
431 int aContextId,
432 std::size_t aMaxDataSize,
433 int aDatapage,
434 const std::string& aPropertyKey)
435 {
436 std::size_t id = property_x("property_get", aPropertyLongName, aStackDepth, aContextId, aMaxDataSize);
437 if (aDatapage >= 0)
438 *theOutStream << " -p " << aDatapage;
439 if (aPropertyKey != "")
440 *theOutStream << " -k " << aPropertyKey;
441 *theOutStream << '\0';
442 theOutStream->flush();
443 return id;
444 }
445
446 std::size_t
property_set(const std::string & aPropertyLongName,int aStackDepth,int aContextId,std::size_t aMaxDataSize,const std::string & aPropertyAddress)447 DebuggerClientImpl::property_set(
448 const std::string& aPropertyLongName,
449 int aStackDepth,
450 int aContextId,
451 std::size_t aMaxDataSize,
452 const std::string& aPropertyAddress)
453 {
454 std::size_t id = property_x("property_set", aPropertyLongName, aStackDepth, aContextId, aMaxDataSize);
455 if (aPropertyAddress != "")
456 *theOutStream << " -a " << aPropertyAddress;
457 *theOutStream << '\0';
458 theOutStream->flush();
459 return id;
460 }
461
462 std::size_t
property_value(const std::string & aPropertyLongName,int aStackDepth,int aContextId,std::size_t aMaxDataSize,int aDatapage,const std::string & aPropertyKey,const std::string & aPropertyAddress)463 DebuggerClientImpl::property_value(
464 const std::string& aPropertyLongName,
465 int aStackDepth,
466 int aContextId,
467 std::size_t aMaxDataSize,
468 int aDatapage,
469 const std::string& aPropertyKey,
470 const std::string& aPropertyAddress)
471 {
472 std::size_t id = property_x("property_get", aPropertyLongName, aStackDepth, aContextId, aMaxDataSize);
473 if (aDatapage >= 0)
474 *theOutStream << " -p " << aDatapage;
475 if (aPropertyKey != "")
476 *theOutStream << " -k " << aPropertyKey;
477 if (aPropertyAddress != "")
478 *theOutStream << " -a " << aPropertyAddress;
479 *theOutStream << '\0';
480 theOutStream->flush();
481 return id;
482 }
483
484 std::size_t
property_x(const std::string & aCommand,const std::string & aPropertyLongName,int aStackDepth,int aContextId,std::size_t aMaxDataSize)485 DebuggerClientImpl::property_x(
486 const std::string& aCommand,
487 const std::string& aPropertyLongName,
488 int aStackDepth,
489 int aContextId,
490 std::size_t aMaxDataSize)
491 {
492 std::size_t id = ++theLastId;
493 *theOutStream << aCommand << " -i " << id << " -n " << aPropertyLongName;
494 if (aStackDepth > 0)
495 *theOutStream << " -d " << aStackDepth;
496 if (aContextId > 0)
497 *theOutStream << " -c " << aContextId;
498 if (aMaxDataSize > 0)
499 *theOutStream << " -m " << aMaxDataSize;
500 theOutStream->flush();
501 return id;
502 }
503
504 std::size_t
source(std::string const & aFile,unsigned int aBeginLine,unsigned int aEndLine)505 DebuggerClientImpl::source(
506 std::string const &aFile,
507 unsigned int aBeginLine,
508 unsigned int aEndLine)
509 {
510 std::size_t id = ++theLastId;
511 *theOutStream << "source -i " << id;
512 // enable zorba extensions
513 *theOutStream << " -z 1";
514 *theOutStream << " -f \"" << aFile << "\"";
515 if (aBeginLine)
516 *theOutStream << " -b " << aBeginLine;
517 if (aEndLine)
518 *theOutStream << " -e " << aEndLine;
519 *theOutStream << '\0';
520 theOutStream->flush();
521 return id;
522 }
523
524 std::size_t
stream_option(OutputStream aStream,StreamBehaviour aBehaviour)525 DebuggerClientImpl::stream_option(OutputStream aStream, StreamBehaviour aBehaviour)
526 {
527 std::size_t id = ++theLastId;
528 switch (aStream) {
529 case Stdout:
530 *theOutStream << "stdout";
531 break;
532 case Stderr:
533 *theOutStream << "stderr";
534 break;
535 case Stdin:
536 *theOutStream << "stdin";
537 break;
538 }
539 *theOutStream << " -i " << id << " -c ";
540 switch (aBehaviour) {
541 case Disable:
542 *theOutStream << "0";
543 break;
544 case CopyData:
545 *theOutStream << "1";
546 break;
547 case Redirection:
548 *theOutStream << "2";
549 break;
550 }
551 *theOutStream << '\0';
552 theOutStream->flush();
553 return id;
554 }
555
556 std::size_t
do_break()557 DebuggerClientImpl::do_break()
558 {
559 std::size_t id = ++theLastId;
560 *theOutStream << "break -i " << id << '\0';
561 theOutStream->flush();
562 return id;
563 }
564
565 std::size_t
eval(std::string const & aExpr)566 DebuggerClientImpl::eval(std::string const &aExpr)
567 {
568 std::size_t id = ++theLastId;
569 *theOutStream << "eval -i " << id << " -- " << encoding::Base64::encode(aExpr.c_str()) << '\0';
570 theOutStream->flush();
571 return id;
572 }
573
574 void
quit()575 DebuggerClientImpl::quit()
576 {
577 theListener->stopLooping();
578 theListener->join();
579 }
580
581 } // namespace zorba
582