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
18 #include "debugger_commons.h"
19
20 #include "system/globalenv.h"
21
22 #include "compiler/api/compilercb.h"
23 #include "context/static_context.h"
24 #include "context/dynamic_context.h"
25 #include "diagnostics/assert.h"
26
27 #include "runtime/debug/debug_iterator.h"
28 #include "runtime/visitors/planiter_visitor.h"
29
30 #include "store/api/item_factory.h"
31
32 #include "zorbatypes/URI.h"
33
34 #include "types/typeimpl.h"
35
36 #include "zorba/util/uri.h"
37
38 #include "debugger_runtime.h"
39
40 namespace zorba {
41
42 // ****************************************************************************
43
SERIALIZABLE_CLASS_VERSIONS(Breakable)44 SERIALIZABLE_CLASS_VERSIONS(Breakable)
45
46 void
47 Breakable::serialize(serialization::Archiver& ar)
48 {
49 ar & theLocation;
50 ar & theEnabled;
51 }
52
53 // ****************************************************************************
54
55 bool
operator ()(const QueryLoc & a,const QueryLoc & b) const56 QueryLocComparator::operator()(const QueryLoc& a, const QueryLoc& b) const
57 {
58 // check the file names
59 // smaller has priority
60 int c;
61 if ((c = a.getFilename().compare(b.getFilename())) != 0) {
62 return c < 0;
63 }
64
65 int ai, bi;
66
67 // check line numbers
68 // smaller has priority
69 ai = a.getLineBegin();
70 bi = b.getLineBegin();
71 if (ai != bi) {
72 return ai < bi;
73 }
74
75 // check column begin numbers
76 // smaller has priority
77 ai = a.getColumnBegin();
78 bi = b.getColumnBegin();
79 if (ai == 0 || bi == 0) {
80 return false;
81 }
82 if (ai != bi) {
83 return ai < bi;
84 }
85
86 // check line end numbers
87 // larger has priority
88 ai = a.getLineEnd();
89 bi = b.getLineEnd();
90 if (ai != bi) {
91 return ai > bi;
92 }
93
94 // check column end numbers
95 // larger has priority
96 ai = a.getColumnEnd();
97 bi = b.getColumnEnd();
98 if (ai == 0 || bi == 0) {
99 return false;
100 }
101
102 return a.getColumnEnd() > b.getColumnEnd();
103 }
104
105 // ****************************************************************************
106
SERIALIZABLE_CLASS_VERSIONS(DebuggerSingletonIterator)107 SERIALIZABLE_CLASS_VERSIONS(DebuggerSingletonIterator)
108
109
110 DebuggerSingletonIterator::DebuggerSingletonIterator(
111 static_context* sctx, QueryLoc loc, DebuggerCommons* lCommons)
112 : NoaryBaseIterator<DebuggerSingletonIterator, PlanIteratorState>(sctx, loc),
113 theCommons(lCommons)
114 {
115 }
116
117 void
serialize(::zorba::serialization::Archiver & ar)118 DebuggerSingletonIterator::serialize(::zorba::serialization::Archiver& ar)
119 {
120 serialize_baseclass(ar, (NoaryBaseIterator<DebuggerSingletonIterator,PlanIteratorState>*)this);
121 ar & theCommons;
122 }
123
124 NOARY_ACCEPT(DebuggerSingletonIterator);
125
126 bool
nextImpl(store::Item_t & result,PlanState & planState) const127 DebuggerSingletonIterator::nextImpl(store::Item_t& result, PlanState& planState) const
128 {
129 PlanIteratorState* state;
130 DEFAULT_STACK_INIT ( PlanIteratorState, state, planState );
131 result = theCommons->getEvalItem();
132 STACK_PUSH ( result != NULL, state );
133 STACK_END (state);
134 }
135
136
137 // ****************************************************************************
138
SERIALIZABLE_CLASS_VERSIONS(DebuggerCommons)139 SERIALIZABLE_CLASS_VERSIONS(DebuggerCommons)
140
141
142 DebuggerCommons::DebuggerCommons(static_context* sctx)
143 : theBreak(false),
144 theCause(0),
145 theBreakCondition(0),
146 theExecEval(false),
147 theStepping(false)
148 {
149 theRuntime = NULL;
150 theCurrentStaticContext = NULL;
151 theCurrentDynamicContext = NULL;
152 thePlanState = NULL;
153 }
154
155
~DebuggerCommons()156 DebuggerCommons::~DebuggerCommons()
157 {
158 }
159
160
161 void
serialize(::zorba::serialization::Archiver & ar)162 DebuggerCommons::serialize(::zorba::serialization::Archiver& ar)
163 {
164 ar & theBreakables;
165
166 if (ar.is_serializing_out())
167 {
168 ar.set_is_temp_field(true);
169 csize s = theBreakableIDs.size();
170 ar & s;
171 ar.set_is_temp_field(false);
172
173 BreakableIdMap::iterator it = theBreakableIDs.begin();
174 BreakableIdMap::iterator end = theBreakableIDs.end();
175
176 for (; it != end; ++it)
177 {
178 QueryLoc loc = (*it).first;
179 ar & loc;
180 ar & (*it).second;
181 }
182 }
183 else
184 {
185 ar.set_is_temp_field(true);
186 csize s;
187 ar & s;
188 ar.set_is_temp_field(false);
189
190 std::pair<QueryLoc, unsigned int> p;
191
192 for (csize i = 0; i < s; ++i)
193 {
194 ar & p.first;
195 ar & p.second;
196
197 theBreakableIDs.insert(p);
198 }
199 }
200
201 ar & theStackTrace;
202 ar & theUriFileMappingMap;
203
204 if (ar.is_serializing_out())
205 theRuntime = NULL;
206
207 ar & theCurrentStaticContext;
208
209 if (ar.is_serializing_out())
210 theCurrentDynamicContext = NULL;
211
212 ar & theBreak;
213 ar & theCause;
214 ar & theIteratorStack;
215 ar & theBreakCondition;
216
217 if (ar.is_serializing_out())
218 thePlanState = NULL;
219
220 ar & theEvalItem;
221 ar & theExecEval;
222 ar & theStepping;
223 }
224
225
226 void
setRuntime(DebuggerRuntime * aRuntime)227 DebuggerCommons::setRuntime(DebuggerRuntime* aRuntime)
228 {
229 theRuntime = aRuntime;
230 }
231
232 DebuggerRuntime*
getRuntime()233 DebuggerCommons::getRuntime()
234 {
235 return theRuntime;
236 }
237
238 void
setCurrentStaticContext(static_context * aStaticContext)239 DebuggerCommons::setCurrentStaticContext(static_context* aStaticContext)
240 {
241 theCurrentStaticContext = aStaticContext;
242 }
243
244 void
setCurrentDynamicContext(dynamic_context * aDynamicContext)245 DebuggerCommons::setCurrentDynamicContext(dynamic_context* aDynamicContext)
246 {
247 theCurrentDynamicContext = aDynamicContext;
248 }
249
250 dynamic_context*
getCurrentDynamicContext() const251 DebuggerCommons::getCurrentDynamicContext() const
252 {
253 return theCurrentDynamicContext;
254 }
255
256 static_context*
getCurrentStaticContext() const257 DebuggerCommons::getCurrentStaticContext() const
258 {
259 return theCurrentStaticContext;
260 }
261
262 unsigned int
addBreakpoint(String & aFileName,unsigned int aLine,bool aEnabled)263 DebuggerCommons::addBreakpoint(String& aFileName, unsigned int aLine, bool aEnabled)
264 {
265 QueryLoc lLocation;
266 lLocation.setLineBegin(aLine);
267 lLocation.setLineEnd(aLine);
268 lLocation.setFilename(aFileName.c_str());
269
270 // we make sure that the this location file name is aligned with the internal ones
271 // it must have a valid URI and a scheme (file://, http://, or https://)
272 adjustLocationFilePath(lLocation);
273
274 BreakableIdMap::iterator lIter = theBreakableIDs.find(lLocation);
275 unsigned int lId;
276
277 if (lIter == theBreakableIDs.end()) {
278 String lFileName = aFileName;
279 // be prepared to throw an exception
280 std::stringstream lSs;
281 lSs << "The breakpoint could not be set at line " << aLine
282 << " in file \"" << lFileName << "\"";
283
284 // let us then try some search before we fail, be good to the user and help him
285 // 1. first we check if he sent a file URI; in this case, sorry!
286 if (lFileName.find("file://") == 0) {
287 throw lSs.str();
288 }
289
290 // now we have to normalize if we hope to find something
291 lFileName = URIHelper::encodeFileURI(lFileName).str();
292 // remove the added file schema prefix
293 // TODO: maybe there is a better way to do this encoding
294 lFileName = lFileName.substr(8);
295
296 // 2. secondly we hope he gave us part of a path of a file
297 lIter = theBreakableIDs.begin();
298 std::vector<std::pair<QueryLoc, int> > lFoundBreakables;
299 zorba::String lFirstBreakablePath;
300 while (lIter != theBreakableIDs.end()) {
301 // for now, only valid if on the breakable is on the same line as requested
302 // TODO: this could be improved if the user wants to add a breakpoint to a line
303 // INSIDE a breakable that spans over multiple lines
304 if (lIter->second != theMainModuleBreakableId && lIter->first.getLineBegin() == aLine) {
305 zorba::String lBreakablePath = lIter->first.getFilename().str();
306
307 // dies the given string matche any part in the breakable file name?
308 if (lBreakablePath.find(lFileName) != String::npos) {
309 // we found the fist candidate path
310 if (lFirstBreakablePath == "") {
311 lFirstBreakablePath = lBreakablePath;
312 }
313 // but stop as soon as we are reaching a second different path (report ambiguity)
314 else if (lFirstBreakablePath != lBreakablePath){
315 lSs.str("");
316 lSs << "The file name \"" << aFileName << "\" is ambiguous. "
317 << "I already found two potential files to set a breakpoint in line " << aLine
318 << ":" << std::endl << " " << lFirstBreakablePath << std::endl << " " << lBreakablePath;
319 throw lSs.str();
320 }
321
322 // Yes! We found one!
323 lFoundBreakables.push_back(std::pair<QueryLoc, int>(lIter->first, lIter->second));
324 }
325 }
326 lIter++;
327 }
328
329 // what should I say, not a very successful search :(
330 if (lFoundBreakables.size() == 0) {
331 throw lSs.str();
332 }
333
334 // TODO: The best solution would be for the debugger to enable all the
335 // matched breakables but the protocol can send back only one ID of the
336 // breakpoint set.
337
338 // so we have multiple breakables, get the first in line
339 // TODO: this does not catch multiple breakables starting in the same line
340 // so only one will be picked (depending how the translator generated them)
341 unsigned int lMinCol = lFoundBreakables.at(0).first.getColumnBegin();
342 lId = lFoundBreakables.at(0).second;
343 for (std::size_t i = 1; i < lFoundBreakables.size(); i++) {
344 if (lMinCol > lFoundBreakables.at(i).first.getColumnBegin()) {
345 lId = lFoundBreakables.at(i).second;
346 }
347 }
348 }
349 else {
350 lId = lIter->second;
351 }
352
353 // now we have a breakable, so set it accordingly
354 theBreakables[lId].setSet(true);
355 theBreakables[lId].setEnabled(aEnabled);
356 return lId;
357 }
358
359 Breakable
getBreakpoint(unsigned int aId)360 DebuggerCommons::getBreakpoint(unsigned int aId)
361 {
362 checkBreakpoint(aId);
363 return theBreakables[aId];
364 }
365
366 BreakableVector
getBreakpoints()367 DebuggerCommons::getBreakpoints()
368 {
369 return theBreakables;
370 }
371
372 void
checkBreakpoint(unsigned int aId)373 DebuggerCommons::checkBreakpoint(unsigned int aId)
374 {
375 if (aId >= theBreakables.size() || !theBreakables[aId].isSet()) {
376 std::stringstream lSs;
377 lSs << "No such breakpoint: " << aId;
378 throw lSs.str();
379 }
380 }
381
382 void
updateBreakpoint(unsigned int aId,bool aEnabled)383 DebuggerCommons::updateBreakpoint(
384 unsigned int aId,
385 bool aEnabled)
386 {
387 checkBreakpoint(aId);
388 theBreakables[aId].setEnabled(aEnabled);
389 }
390
391 void
updateBreakpoint(unsigned int aId,bool aEnabled,std::string aCondition,unsigned int aHitValue)392 DebuggerCommons::updateBreakpoint(
393 unsigned int aId,
394 bool aEnabled,
395 std::string aCondition,
396 unsigned int aHitValue)
397 {
398 checkBreakpoint(aId);
399 theBreakables[aId].setEnabled(aEnabled);
400 // TODO: set condition
401 }
402
403 void
removeBreakpoint(unsigned int aId)404 DebuggerCommons::removeBreakpoint(unsigned int aId)
405 {
406 checkBreakpoint(aId);
407 theBreakables[aId].setSet(false);
408 }
409
410 bool
canBreak()411 DebuggerCommons::canBreak()
412 {
413 return !theExecEval;
414 }
415
416 bool
mustBreak(SuspensionCause & aCause)417 DebuggerCommons::mustBreak(SuspensionCause& aCause)
418 {
419 if (theRuntime->getAndClearInterruptBreak()) {
420 aCause = CAUSE_USER;
421 return true;
422 }
423 if (theBreak) {
424 aCause = theCause;
425 return true;
426 } else if (theStepping) {
427 std::size_t lSize = theIteratorStack.size();
428
429 // either we meet the step condition
430 if (lSize <= theBreakCondition) {
431 // reset the break conditions
432 theBreakCondition = 0;
433 theStepping = false;
434 return true;
435 }
436 // or we have stepped over from a function declaration breakpoint
437 // we have for sure at least the following stack of debug iterators:
438 // MainModule DI -> FunctionCall DI -> FunctionDecl DI -> current DI
439 if (lSize > 3 && lSize == theBreakCondition + 1 && theIteratorStack.at(lSize - 2)->getDebuggerParent() == NULL) {
440 // reset the break conditions
441 theBreakCondition = 0;
442 theStepping = false;
443 return true;
444 }
445 // or we have stepped over and we reached another variable declaration
446 // TODO: still to check and stop only at declarations on the same level
447 if (lSize <= theBreakCondition && theIteratorStack.back()->isVarDeclaration()) {
448 // reset the break conditions
449 theBreakCondition = 0;
450 theStepping = false;
451 return true;
452 }
453 }
454
455 return false;
456 }
457
458 bool
hasToBreakAt(QueryLoc aLocation)459 DebuggerCommons::hasToBreakAt(QueryLoc aLocation)
460 {
461 // we make sure that this location file name is a valid URI and has a scheme (file://, http://, or https://)
462 adjustLocationFilePath(aLocation);
463
464 BreakableIdMap::const_iterator lIter = theBreakableIDs.find(aLocation);
465 if (lIter == theBreakableIDs.end()) {
466 return false;
467 }
468
469 Breakable lBreakable = theBreakables[lIter->second];
470 return lBreakable.isSet() && lBreakable.isEnabled();
471 }
472
473 bool
hasToBreakAt(const DebugIterator * aIter)474 DebuggerCommons::hasToBreakAt(const DebugIterator* aIter)
475 {
476 return false;
477 }
478
479 void
setBreak(bool lBreak,SuspensionCause aCause)480 DebuggerCommons::setBreak(bool lBreak, SuspensionCause aCause)
481 {
482 #ifndef NDEBUG
483 //Check preconditions
484 ZORBA_ASSERT(lBreak ? aCause != 0 : true);
485 #endif // NDEBUG
486 theBreak = lBreak;
487 theCause = aCause;
488 #ifndef NDEBUG
489 // Check post conditions
490 ZORBA_ASSERT(theBreak == lBreak);
491 ZORBA_ASSERT(theCause == aCause);
492 #endif //NDEBUG
493 }
494
495 void
setCurrentIterator(const DebugIterator * aIterator)496 DebuggerCommons::setCurrentIterator(const DebugIterator* aIterator)
497 {
498 // don't modify the iterator stack during expression evaluation
499 if (theExecEval) {
500 return;
501 }
502
503 const DebugIterator* lParent = aIterator->getDebuggerParent();
504
505 // when the stack is empty (main module debug iterator) or
506 // when the parent is NULL but not a variable declaration (function declaration iterator) or
507 // when the parent is the last in the stack
508 if (theIteratorStack.empty() ||
509 (lParent == NULL && !aIterator->isVarDeclaration()) ||
510 theIteratorStack.back() == lParent) {
511 theIteratorStack.push_back((DebugIterator*)aIterator);
512 return;
513 }
514
515 // avoid multiple calls to this function
516 // recursive functions always insert at least 2 iterators
517 if (theIteratorStack.back() == aIterator) {
518 return;
519 }
520
521 // now we remove the iterators from the stack until we find the parent
522 theIteratorStack.pop_back();
523 while (!theIteratorStack.empty()) {
524 if (theIteratorStack.back() == aIterator->getDebuggerParent()) {
525 theIteratorStack.push_back((DebugIterator*)aIterator);
526 return;
527 }
528 theIteratorStack.pop_back();
529 }
530
531 // noe the stack is empty, so push this iterator in the stack
532 theIteratorStack.push_back((DebugIterator*)aIterator);
533 }
534
535 const DebugIterator*
getCurrentIterator() const536 DebuggerCommons::getCurrentIterator() const
537 {
538 if (theIteratorStack.empty()) {
539 return NULL;
540 }
541 return theIteratorStack.back();
542 }
543
544 void
makeStepOver()545 DebuggerCommons::makeStepOver()
546 {
547 theStepping = true;
548 if (!theIteratorStack.empty()) {
549 theBreakCondition = theIteratorStack.size();
550 } else {
551 theBreakCondition = 0;
552 }
553 }
554
makeStepOut()555 void DebuggerCommons::makeStepOut()
556 {
557 theStepping = true;
558 if (!theIteratorStack.empty()) {
559 theBreakCondition = theIteratorStack.size() - 1;
560 } else {
561 theBreakCondition = 0;
562 }
563 }
564
565 void
setPlanState(PlanState * aPlanState)566 DebuggerCommons::setPlanState(PlanState* aPlanState)
567 {
568 thePlanState = aPlanState;
569 //Check postconditions
570 ZORBA_ASSERT(thePlanState == aPlanState);
571 }
572
573 std::list<std::pair<zstring, zstring> >
eval(const zstring & aExpr,Zorba_SerializerOptions & aSerOpts)574 DebuggerCommons::eval(const zstring& aExpr, Zorba_SerializerOptions& aSerOpts)
575 {
576 theExecEval = true;
577 zstring lStore = aExpr;
578 GlobalEnvironment::getInstance().getItemFactory()->createString(theEvalItem,
579 lStore);
580 std::list<std::pair<zstring, zstring> > lRes;
581 try {
582 lRes = theIteratorStack.back()->eval(thePlanState, &aSerOpts);
583 theExecEval = false;
584 } catch (...) {
585 theExecEval = false;
586 throw;
587 }
588
589 return lRes;
590 }
591
592 store::Item_t
getEvalItem()593 DebuggerCommons::getEvalItem()
594 {
595 return theEvalItem;
596 }
597
598 void
addModuleUriMapping(std::string aUri,std::string aFileUri)599 DebuggerCommons::addModuleUriMapping(std::string aUri, std::string aFileUri)
600 {
601 ZORBA_ASSERT(theUriFileMappingMap.find(aUri) == theUriFileMappingMap.end());
602 theUriFileMappingMap.insert(
603 std::pair<std::string, std::string>(aUri, aFileUri));
604 ZORBA_ASSERT(theUriFileMappingMap.find(aUri) != theUriFileMappingMap.end());
605 }
606
607 std::string
getFilepathOfURI(const std::string & aUri) const608 DebuggerCommons::getFilepathOfURI(const std::string& aUri) const
609 {
610 std::map<std::string, std::string>::const_iterator lIter;
611 lIter = theUriFileMappingMap.find(aUri);
612 zstring lString;
613 if (lIter == theUriFileMappingMap.end())
614 {
615 lString = aUri;
616 }
617 else
618 {
619 lString = lIter->second;
620 }
621 zstring lRes;
622 URI::decode_file_URI(lString, lRes);
623 return lRes.str();
624 }
625
626 void
addBreakable(Breakable & aBreakable,bool aIsMainModuleBreakable)627 DebuggerCommons::addBreakable(
628 Breakable& aBreakable,
629 bool aIsMainModuleBreakable)
630 {
631 // we make sure that this breakable file name is a valid URI and has a scheme (file://, http://, or https://)
632 adjustLocationFilePath(aBreakable.getLocation());
633
634 unsigned int lId = theBreakables.size();
635 if (aIsMainModuleBreakable) {
636 theMainModuleBreakableId = lId;
637 }
638 theBreakables.push_back(aBreakable);
639 theBreakableIDs[aBreakable.getLocation()] = lId;
640 }
641
642 void
pushStackFrame(QueryLoc aLocation,std::string & aFunctionName)643 DebuggerCommons::pushStackFrame(QueryLoc aLocation, std::string& aFunctionName)
644 {
645 // we make sure that the stack frame locations always have valid URIs and a scheme (file://, http://, or https://)
646 adjustLocationFilePath(aLocation);
647
648 theStackTrace.push_back(std::pair<QueryLoc, std::string>(aLocation, aFunctionName));
649 }
650
651 void
popStackFrame()652 DebuggerCommons::popStackFrame()
653 {
654 theStackTrace.pop_back();
655 }
656
657 std::vector<std::pair<QueryLoc, std::string> >
getStackFrames() const658 DebuggerCommons::getStackFrames() const
659 {
660 return theStackTrace;
661 }
662
663 void
adjustLocationFilePath(QueryLoc & aLocation)664 DebuggerCommons::adjustLocationFilePath(QueryLoc& aLocation)
665 {
666 zstring lOldFilename(aLocation.getFilename());
667 zstring lPrefix = lOldFilename.substr(0, 7);
668
669 if (lPrefix == "file://") {
670 #ifdef WIN32
671 // decode and encode back to solve the driver column encoding: C:, D:, etc.
672 const String lNewFilename = URIHelper::decodeFileURI(lOldFilename.str());
673 const String lNewURI = URIHelper::encodeFileURI(lNewFilename);
674 aLocation.setFilename(lNewURI.str());
675 #endif
676 return;
677 }
678
679 // just encode and assume file for non-URI locations
680 if (lPrefix != "http://" && lPrefix != "https:/") {
681 const String lNewURI = URIHelper::encodeFileURI(lOldFilename.str());
682 aLocation.setFilename(lNewURI.str());
683 return;
684 }
685 }
686
687 } // namespace zorba
688