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