roxiedebug.cpp 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jexcept.hpp"
  14. #include "thorherror.h"
  15. #include "roxiehelper.hpp"
  16. #include "roxiedebug.hpp"
  17. #include "portlist.h"
  18. #include "eclrtl.hpp"
  19. #include "deftype.hpp"
  20. #include "eclhelper.hpp"
  21. #include "thorcommon.hpp"
  22. //=======================================================================================
  23. bool CDebugCommandHandler::checkCommand(IXmlWriter &out, const char *&supplied, const char *expected)
  24. {
  25. unsigned i = 0;
  26. loop
  27. {
  28. if (!supplied[i])
  29. {
  30. out.outputBeginNested(expected, true);
  31. supplied = expected;
  32. return true;
  33. }
  34. if (!expected[i])
  35. return false;
  36. if (supplied[i] != expected[i])
  37. return false;
  38. i++;
  39. }
  40. }
  41. void CDebugCommandHandler::doDebugCommand(IPropertyTree *query, IDebuggerContext *debugContext, FlushingStringBuffer &output)
  42. {
  43. const char *commandName = query->queryName();
  44. CommonXmlWriter out(0, 1);
  45. if (strnicmp(commandName, "b", 1)==0 && checkCommand(out, commandName, "breakpoint"))
  46. {
  47. const char *mode = query->queryProp("@mode");
  48. const char *id = query->queryProp("@id");
  49. const char *action = query->queryProp("@action");
  50. const char *fieldName = query->queryProp("@fieldName");
  51. const char *condition = query->queryProp("@condition");
  52. bool caseSensitive = query->getPropBool("@caseSensitive", false);
  53. const char *value = query->queryProp("@value");
  54. unsigned rowCount = query->getPropInt("@rowCount", 0);
  55. const char *rowCountMode = query->queryProp("@rowCountMode");
  56. debugContext->addBreakpoint(&out, mode, id, action, fieldName, condition, value, caseSensitive, rowCount, rowCountMode);
  57. }
  58. else if (strnicmp(commandName, "c", 1)==0 && checkCommand(out, commandName, "continue"))
  59. {
  60. const char *mode = query->queryProp("@mode");
  61. const char *id = query->queryProp("@id");
  62. debugContext->debugContinue(&out, mode, id);
  63. }
  64. else if (strnicmp(commandName, "c", 1)==0 && checkCommand(out, commandName, "changes"))
  65. {
  66. unsigned sequence = query->getPropInt("@sequence", -1);
  67. debugContext->debugChanges(&out, sequence);
  68. }
  69. else if (strnicmp(commandName, "c", 1)==0 && checkCommand(out, commandName, "counts"))
  70. {
  71. // MORE It could be argued that this is really more a "changes" with a "global" vs "active" option....
  72. unsigned sequence = query->getPropInt("@sequence", -1);
  73. debugContext->debugCounts(&out, sequence, false);
  74. }
  75. else if (strnicmp(commandName, "d", 1)==0 && checkCommand(out, commandName, "delete"))
  76. {
  77. const char *idx = query->queryProp("@idx");
  78. if (!idx)
  79. throw MakeStringException(THORHELPER_DEBUG_ERROR, "delete must specify breakpoint index, or all");
  80. if (stricmp(idx, "all")==0)
  81. debugContext->removeAllBreakpoints(&out);
  82. else
  83. {
  84. char *ep;
  85. unsigned bpIdx = strtoul(idx, &ep, 10);
  86. if (ep != idx && *ep==0)
  87. {
  88. if (bpIdx)
  89. debugContext->removeBreakpoint(&out, bpIdx);
  90. else
  91. debugContext->removeAllBreakpoints(&out);
  92. }
  93. else
  94. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Invalid value for idx - expected number or all");
  95. }
  96. }
  97. else if (strnicmp(commandName, "g", 1)==0 && checkCommand(out, commandName, "graph"))
  98. {
  99. const char *graphName = query->queryProp("@name");
  100. if (!graphName)
  101. {
  102. bool original = query->getPropBool("@original", true);
  103. debugContext->getCurrentGraphXGMML(&out, original);
  104. }
  105. else if (stricmp(graphName, "all")==0)
  106. debugContext->getQueryXGMML(&out);
  107. else
  108. debugContext->getGraphXGMML(&out, graphName);
  109. }
  110. else if (strnicmp(commandName, "g", 1)==0 && checkCommand(out, commandName, "get"))
  111. {
  112. const char *name = query->queryProp("@name");
  113. const char *id = query->queryProp("@id");
  114. debugContext->debugGetConfig(&out, name, id);
  115. }
  116. else if (strnicmp(commandName, "i", 1)==0 && checkCommand(out, commandName, "interrupt"))
  117. {
  118. debugContext->debugInterrupt(&out);
  119. }
  120. else if (strnicmp(commandName, "l", 1)==0 && checkCommand(out, commandName, "list"))
  121. {
  122. const char *idx = query->queryProp("@idx");
  123. if (!idx || strcmp(idx, "all")==0)
  124. debugContext->listAllBreakpoints(&out);
  125. else
  126. {
  127. char *ep;
  128. unsigned bpIdx = strtoul(idx, &ep, 10);
  129. if (ep != idx && *ep==0)
  130. debugContext->listBreakpoint(&out, bpIdx);
  131. else
  132. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Invalid value for idx - expected number or all");
  133. }
  134. }
  135. else if (strnicmp(commandName, "n", 1)==0 && checkCommand(out, commandName, "next"))
  136. {
  137. debugContext->debugNext(&out);
  138. }
  139. else if (strnicmp(commandName, "o", 1)==0 && checkCommand(out, commandName, "over"))
  140. {
  141. debugContext->debugOver(&out);
  142. }
  143. else if (strnicmp(commandName, "p", 1)==0 && checkCommand(out, commandName, "print"))
  144. {
  145. const char *edgeId = query->queryProp("@edgeId");
  146. unsigned numRows = query->getPropInt("@numRows", 1);
  147. unsigned startRow= query->getPropInt("@startRow", 0);
  148. debugContext->debugPrint(&out, edgeId, startRow, numRows);
  149. }
  150. else if (strnicmp(commandName, "q", 1)==0 && checkCommand(out, commandName, "quit"))
  151. {
  152. debugContext->debugQuit(&out);
  153. }
  154. else if (strnicmp(commandName, "r", 1)==0 && checkCommand(out, commandName, "run"))
  155. {
  156. debugContext->debugRun(&out);
  157. }
  158. else if (strnicmp(commandName, "s", 1)==0 && checkCommand(out, commandName, "step"))
  159. {
  160. const char *mode = query->queryProp("@mode");
  161. debugContext->debugStep(&out, mode);
  162. }
  163. else if (strnicmp(commandName, "s", 1)==0 && checkCommand(out, commandName, "status"))
  164. {
  165. debugContext->debugStatus(&out);
  166. }
  167. else if (strnicmp(commandName, "s", 1)==0 && checkCommand(out, commandName, "skip"))
  168. {
  169. debugContext->debugSkip(&out);
  170. }
  171. else if (strnicmp(commandName, "s", 1)==0 && checkCommand(out, commandName, "search"))
  172. {
  173. const char *fieldName = query->queryProp("@fieldName");
  174. const char *condition = query->queryProp("@condition");
  175. if (!condition)
  176. condition="contains";
  177. const char *value = query->queryProp("@value");
  178. bool caseSensitive = query->getPropBool("@caseSensitive", false);
  179. bool fullRows = query->getPropBool("@fullRows", false);
  180. debugContext->debugSearch(&out, fieldName, condition, value, caseSensitive, fullRows);
  181. }
  182. else if (strnicmp(commandName, "s", 1)==0 && checkCommand(out, commandName, "set"))
  183. {
  184. const char *name = query->queryProp("@name");
  185. const char *value = query->queryProp("@value");
  186. const char *id = query->queryProp("@id");
  187. debugContext->debugSetConfig(&out, name, value, id);
  188. }
  189. else if (strnicmp(commandName, "v", 1)==0 && checkCommand(out, commandName, "variable"))
  190. {
  191. const char *name = query->queryProp("@name");
  192. const char *type = query->queryProp("@type");
  193. debugContext->debugPrintVariable(&out, name, type);
  194. }
  195. else if (strnicmp(commandName, "w", 1)==0 && checkCommand(out, commandName, "where"))
  196. {
  197. debugContext->debugWhere(&out);
  198. }
  199. else
  200. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Unknown command %s", commandName);
  201. out.outputEndNested(commandName);
  202. output.append(out.str());
  203. }
  204. //=======================================================================================
  205. MAKEValueArray(const RtlTypeInfo *, RtlTypeInfoArray);
  206. class SimpleFieldSearcher : public CInterface, implements IRowMatcher
  207. {
  208. protected:
  209. unsigned searchStringLength;
  210. const char *searchString;
  211. StringAttr expression;
  212. StringAttr searchFieldName;
  213. MemoryBuffer searchQString;
  214. bool matchSeen;
  215. bool boolValueSet;
  216. bool intValueSet;
  217. bool uintValueSet;
  218. bool realValueSet;
  219. bool decimalValueSet;
  220. bool udecimalValueSet;
  221. bool qstringValueSet;
  222. bool boolValue;
  223. __int64 intValue;
  224. double realValue;
  225. BreakpointConditionMode mode;
  226. bool caseSensitive;
  227. RtlTypeInfoArray recordTypesCanMatch;
  228. RtlTypeInfoArray recordTypesCannotMatch;
  229. bool checkCondition(int diff)
  230. {
  231. switch (mode)
  232. {
  233. case BreakpointConditionLess:
  234. return (diff < 0);
  235. case BreakpointConditionLessEqual:
  236. return (diff <= 0);
  237. case BreakpointConditionGreater:
  238. return (diff > 0);
  239. case BreakpointConditionGreaterEqual:
  240. return (diff >= 0);
  241. case BreakpointConditionNotEqual:
  242. return (diff != 0);
  243. default:
  244. return (diff == 0);
  245. }
  246. }
  247. inline bool checkFieldName(const char *fieldname)
  248. {
  249. return searchFieldName.isEmpty() || stricmp(searchFieldName, fieldname)==0;
  250. }
  251. public:
  252. SimpleFieldSearcher(const char *_searchFieldName, const char *_expression, BreakpointConditionMode _mode, bool _caseSensitive)
  253. : searchFieldName(_searchFieldName), expression(_expression), mode(_mode), caseSensitive(_caseSensitive)
  254. {
  255. searchString = expression.get();
  256. searchStringLength = expression.length();
  257. matchSeen = false;
  258. boolValueSet = false;
  259. intValueSet = false;
  260. uintValueSet = false;
  261. realValueSet = false;
  262. decimalValueSet = false;
  263. udecimalValueSet = false;
  264. qstringValueSet = false;
  265. if (searchString)
  266. {
  267. if (stricmp(searchString, "true")==0)
  268. boolValueSet = boolValue = true;
  269. else if (stricmp(searchString, "false")==0)
  270. {
  271. boolValueSet = true; boolValue = false;
  272. }
  273. else
  274. {
  275. // MORE - should really support decimal, real, and perhaps esoteric forms of integer (0x.... etc) here....
  276. const char *v = searchString;
  277. bool isNegative = false;
  278. while (isspace(*v))
  279. v++;
  280. if (*v=='-')
  281. {
  282. isNegative = true;
  283. v++;
  284. }
  285. if (isdigit(*v))
  286. {
  287. intValue = 0;
  288. char c;
  289. loop
  290. {
  291. c = *v++;
  292. if ((c >= '0') && (c <= '9'))
  293. intValue = intValue * 10 + (c-'0');
  294. else
  295. break;
  296. }
  297. switch (c)
  298. {
  299. case ' ':
  300. while (isspace(c = *v))
  301. v++;
  302. if (c)
  303. break;
  304. // fall into...
  305. case '\0':
  306. intValueSet = true;
  307. if (isNegative)
  308. intValue = -intValue;
  309. else
  310. uintValueSet = true;
  311. break;
  312. // MORE - want something like...
  313. // case '.':
  314. // TempDecimal.setString().getDecimal()
  315. }
  316. }
  317. }
  318. }
  319. }
  320. virtual bool matched() const { return matchSeen; }
  321. virtual void reset() { matchSeen = false; }
  322. virtual const char *queryFieldName() const { return searchFieldName; }
  323. virtual const char *queryValue() const { return expression; }
  324. virtual bool queryCaseSensitive() const { return caseSensitive; }
  325. virtual void serialize(MemoryBuffer &out) const
  326. {
  327. out.append(searchFieldName).append((char) mode).append(expression).append(caseSensitive);
  328. }
  329. bool canMatchAny(const RtlTypeInfo *recordType)
  330. {
  331. bool sawMatch = false;
  332. ForEachItemIn (idx1, recordTypesCanMatch)
  333. {
  334. if (recordTypesCanMatch.item(idx1) == recordType)
  335. {
  336. if (idx1)
  337. recordTypesCanMatch.swap(0, idx1);
  338. return true;
  339. }
  340. }
  341. ForEachItemIn (idx2, recordTypesCannotMatch)
  342. {
  343. if (recordTypesCannotMatch.item(idx2) == recordType)
  344. {
  345. if (idx2)
  346. recordTypesCannotMatch.swap(0, idx2);
  347. return false;
  348. }
  349. }
  350. const RtlFieldInfo * const * fields = recordType->queryFields();
  351. assertex(fields);
  352. while (*fields)
  353. {
  354. const RtlTypeInfo *childType = fields[0]->type->queryChildType();
  355. if (childType)
  356. {
  357. // MORE - get funky with fieldname.fieldname ? Possible future feature....
  358. if (canMatchAny(childType))
  359. sawMatch = true;
  360. }
  361. if (stricmp(fields[0]->name->getAtomNamePtr(), searchFieldName)==0)
  362. sawMatch = true;
  363. fields++;
  364. }
  365. if (sawMatch)
  366. recordTypesCanMatch.add(recordType, 0);
  367. else
  368. recordTypesCannotMatch.add(recordType, 0);
  369. return sawMatch;
  370. }
  371. virtual bool canMatchAny(IOutputMetaData *meta)
  372. {
  373. bool canMatch = true; // safest assumption...
  374. if (searchFieldName.length())
  375. {
  376. const RtlTypeInfo *typeInfo = meta->queryTypeInfo();
  377. assertex(typeInfo);
  378. if (!canMatchAny(typeInfo))
  379. canMatch = false;
  380. }
  381. return canMatch;
  382. }
  383. IMPLEMENT_IINTERFACE;
  384. virtual void outputInlineXml(const char *xml){}
  385. virtual void outputQuoted(const char *text) { }
  386. virtual void outputString(unsigned len, const char *field, const char *fieldname)
  387. {
  388. if (!matchSeen && checkFieldName(fieldname) && (len >= searchStringLength))
  389. {
  390. int diff = caseSensitive ? memcmp(field,searchString,searchStringLength) : memicmp(field,searchString,searchStringLength);
  391. if (!diff) // little bit suboptimal as some modes don't care about difference between equal and gt - but who cares!
  392. {
  393. // if any remaining char is not space, it's gt
  394. unsigned i = searchStringLength;
  395. while (i < len)
  396. {
  397. if (field[i] != ' ')
  398. {
  399. diff++;
  400. break;
  401. }
  402. i++;
  403. }
  404. }
  405. matchSeen = checkCondition(diff);
  406. }
  407. }
  408. virtual void outputBool(bool field, const char *fieldname)
  409. {
  410. if (boolValueSet && !matchSeen && checkFieldName(fieldname))
  411. {
  412. matchSeen = checkCondition((int) field - (int) boolValue);
  413. }
  414. }
  415. virtual void outputData(unsigned len, const void *field, const char *fieldname)
  416. {
  417. // MORE
  418. }
  419. virtual void outputInt(__int64 field, const char *fieldname)
  420. {
  421. if (intValueSet && !matchSeen && checkFieldName(fieldname))
  422. {
  423. matchSeen = checkCondition((field == intValue) ? 0 : ((field > intValue) ? 1 : -1));
  424. }
  425. }
  426. virtual void outputUInt(unsigned __int64 field, const char *fieldname)
  427. {
  428. // NOTE - "contains" is interpreted as "equals" on numeric fields
  429. if (uintValueSet && !matchSeen && checkFieldName(fieldname))
  430. {
  431. matchSeen = checkCondition((field == intValue) ? 0 : ((field > (unsigned __int64) intValue) ? 1 : -1));
  432. }
  433. }
  434. virtual void outputReal(double field, const char *fieldname)
  435. {
  436. // NOTE - "contains" is interpreted as "equals" on numeric fields
  437. if (realValueSet && !matchSeen && checkFieldName(fieldname))
  438. {
  439. matchSeen = checkCondition((field == realValue) ? 0 : ((field > realValue) ? 1 : -1));
  440. }
  441. }
  442. virtual void outputDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  443. {
  444. // Searching/breaking on decimal not supported at the moment
  445. }
  446. virtual void outputUDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  447. {
  448. // Searching/breaking on decimal not supported at the moment
  449. }
  450. virtual void outputUnicode(unsigned len, const UChar *field, const char *fieldname)
  451. {
  452. // Searching/breaking on unicode not supported at the moment
  453. }
  454. virtual void outputQString(unsigned len, const char *field, const char *fieldname)
  455. {
  456. if (!matchSeen && checkFieldName(fieldname))
  457. {
  458. if (!qstringValueSet)
  459. {
  460. size32_t outlen;
  461. char *out;
  462. rtlStrToQStrX(outlen, out, searchStringLength, searchString);
  463. searchQString.setBuffer(outlen, out, true);
  464. }
  465. matchSeen = checkCondition(rtlCompareQStrQStr(len, field, searchQString.length(), searchQString.toByteArray()));
  466. }
  467. }
  468. virtual void outputUtf8(unsigned len, const char *field, const char *fieldname)
  469. {
  470. // Searching/breaking on unicode not supported at the moment
  471. }
  472. virtual void outputBeginDataset(const char *dsname, bool nestChildren)
  473. {
  474. // nothing for now
  475. }
  476. virtual void outputEndDataset(const char *dsname)
  477. {
  478. // nothing for now
  479. }
  480. virtual void outputBeginNested(const char *fieldname, bool nestChildren)
  481. {
  482. // nothing for now
  483. }
  484. virtual void outputEndNested(const char *fieldname)
  485. {
  486. // nothing for now
  487. }
  488. virtual void outputBeginArray(const char *fieldname)
  489. {
  490. // nothing for now
  491. }
  492. virtual void outputEndArray(const char *fieldname)
  493. {
  494. // nothing for now
  495. }
  496. virtual void outputSetAll()
  497. {
  498. // nothing for now
  499. }
  500. virtual void outputXmlns(const char *prefix, const char *uri)
  501. {
  502. // nothing for now
  503. }
  504. };
  505. class ContainsFieldSearcher : public SimpleFieldSearcher
  506. {
  507. // For most field types, just use equals version. Only meaningful for string types (and only implemented so far for plain ascii)
  508. public:
  509. ContainsFieldSearcher(const char *_searchFieldName, const char *_expression, bool _caseSensitive=true) : SimpleFieldSearcher(_searchFieldName, _expression, BreakpointConditionContains, _caseSensitive)
  510. {
  511. // This is where you set up Boyer-Moore tables...
  512. }
  513. virtual void outputString(unsigned len, const char *field, const char *fieldname)
  514. {
  515. if (!matchSeen && checkFieldName(fieldname) && (len >= searchStringLength)) // could swap those last two tests - last is faster but filters less out...
  516. {
  517. if (searchStringLength==1)
  518. {
  519. if (memchr(field, searchString[0], len))
  520. matchSeen = true;
  521. }
  522. else
  523. {
  524. unsigned steps = len-searchStringLength+1;
  525. for ( unsigned i = 0; i < steps; i++ )
  526. {
  527. if ( !memcmp((char *)field+i,searchString,searchStringLength) ) // MORE - Should I use Boyer-Moore? Probably. Is it worth it in (typically) small search targets?
  528. {
  529. matchSeen = true;
  530. return;
  531. }
  532. }
  533. }
  534. }
  535. }
  536. };
  537. class ContainsNoCaseFieldSearcher : public ContainsFieldSearcher
  538. {
  539. // For most field types, just use equals version. Only meaningful for string types (and only implemented so far for plain ascii)
  540. public:
  541. ContainsNoCaseFieldSearcher(const char *_searchFieldName, const char *_expression) : ContainsFieldSearcher(_searchFieldName, _expression, false)
  542. {
  543. }
  544. virtual void outputString(unsigned len, const char *field, const char *fieldname)
  545. {
  546. if (!matchSeen && checkFieldName(fieldname) && (len >= searchStringLength))
  547. {
  548. unsigned steps = len-searchStringLength+1;
  549. for ( unsigned i = 0; i < steps; i++ )
  550. {
  551. if ( !memicmp((char *)field+i,searchString,searchStringLength) )
  552. {
  553. matchSeen = true;
  554. return;
  555. }
  556. }
  557. }
  558. }
  559. };
  560. class StartsWithFieldSearcher : public SimpleFieldSearcher
  561. {
  562. // For most field types, just use equals version. Only meaningful for string types (and only implemented so far for plain ascii)
  563. public:
  564. StartsWithFieldSearcher(const char *_searchFieldName, const char *_expression, bool _caseSensitive) : SimpleFieldSearcher(_searchFieldName, _expression, BreakpointConditionStartsWith, _caseSensitive)
  565. {
  566. }
  567. virtual void outputString(unsigned len, const char *field, const char *fieldname)
  568. {
  569. if (!matchSeen && checkFieldName(fieldname) && (len >= searchStringLength))
  570. {
  571. int diff = caseSensitive ? memcmp(field,searchString,searchStringLength) : memicmp(field,searchString,searchStringLength);
  572. if (diff == 0)
  573. matchSeen = true;
  574. }
  575. }
  576. };
  577. extern IRowMatcher *createRowMatcher(const char *fieldName, BreakpointConditionMode condition, const char *value, bool caseSensitive)
  578. {
  579. switch (condition)
  580. {
  581. case BreakpointConditionNone:
  582. case BreakpointConditionEOG:
  583. case BreakpointConditionEOF:
  584. return NULL;
  585. case BreakpointConditionLess:
  586. case BreakpointConditionLessEqual:
  587. case BreakpointConditionGreater:
  588. case BreakpointConditionGreaterEqual:
  589. case BreakpointConditionNotEqual:
  590. case BreakpointConditionEquals:
  591. return new SimpleFieldSearcher(fieldName, value, condition, caseSensitive);
  592. case BreakpointConditionContains:
  593. if (caseSensitive)
  594. return new ContainsFieldSearcher(fieldName, value);
  595. else
  596. return new ContainsNoCaseFieldSearcher(fieldName, value);
  597. case BreakpointConditionStartsWith:
  598. return new StartsWithFieldSearcher(fieldName, value, caseSensitive);
  599. default:
  600. throwUnexpected();
  601. }
  602. }
  603. extern IRowMatcher *createRowMatcher(MemoryBuffer &serialized)
  604. {
  605. StringAttr fieldName;
  606. char condition;
  607. StringAttr value;
  608. bool caseSensitive;
  609. serialized.read(fieldName).read(condition).read(value).read(caseSensitive);
  610. return createRowMatcher(fieldName, (BreakpointConditionMode) condition, value, caseSensitive);
  611. }
  612. //===============================================================================================
  613. CBreakpointInfo::CBreakpointInfo(BreakpointMode _mode, const char *_id, BreakpointActionMode _action,
  614. const char *_fieldName, BreakpointConditionMode _condition, const char *_value, bool _caseSensitive,
  615. unsigned _rowCount, BreakpointCountMode _rowCountMode)
  616. : mode(_mode), id(_id), action(_action), condition(_condition),
  617. rowCount(_rowCount), rowCountMode(_rowCountMode)
  618. {
  619. uid = nextUID();
  620. rowMatcher.setown(createRowMatcher(_fieldName, _condition, _value, _caseSensitive));
  621. }
  622. CBreakpointInfo::CBreakpointInfo(BreakpointMode _mode) : mode(_mode)
  623. {
  624. uid = 0; // reserved for the dummy breakpoint
  625. action = BreakpointActionContinue;
  626. rowCount = 0;
  627. rowCountMode = BreakpointCountNone;
  628. condition = BreakpointConditionNone;
  629. }
  630. CBreakpointInfo::CBreakpointInfo(MemoryBuffer &from)
  631. {
  632. from.read(uid);
  633. from.read(id);
  634. char temp;
  635. from.read(temp); mode = (BreakpointMode) temp;
  636. from.read(temp); action = (BreakpointActionMode) temp;
  637. from.read(rowCount);
  638. from.read(temp); rowCountMode = (BreakpointCountMode) temp;
  639. from.read(temp); condition = (BreakpointConditionMode) temp;
  640. switch (condition)
  641. {
  642. case BreakpointConditionNone:
  643. case BreakpointConditionEOG:
  644. case BreakpointConditionEOF:
  645. break;
  646. default:
  647. StringAttr fieldName, value;
  648. bool caseSensitive;
  649. from.read(fieldName);
  650. from.read(value);
  651. from.read(caseSensitive);
  652. rowMatcher.setown(createRowMatcher(fieldName, condition, value, caseSensitive));
  653. }
  654. }
  655. void CBreakpointInfo::serialize(MemoryBuffer &to) const
  656. {
  657. to.append(uid);
  658. to.append(id);
  659. to.append((char) mode);
  660. to.append((char) action);
  661. to.append(rowCount);
  662. to.append((char) rowCountMode);
  663. to.append((char) condition);
  664. if (rowMatcher)
  665. {
  666. to.append(rowMatcher->queryFieldName());
  667. to.append(rowMatcher->queryValue());
  668. to.append(rowMatcher->queryCaseSensitive());
  669. }
  670. }
  671. SpinLock CBreakpointInfo::UIDlock;
  672. unsigned CBreakpointInfo::nextUIDvalue;
  673. unsigned CBreakpointInfo::queryUID() const
  674. {
  675. return uid;
  676. }
  677. void CBreakpointInfo::noteEdge(IActivityDebugContext &edge)
  678. {
  679. activeEdges.append(edge);
  680. }
  681. void CBreakpointInfo::removeEdge(IActivityDebugContext &edge)
  682. {
  683. activeEdges.zap(edge);
  684. }
  685. bool CBreakpointInfo::equals(IBreakpointInfo &other) const
  686. {
  687. CBreakpointInfo *bp = QUERYINTERFACE(&other, CBreakpointInfo);
  688. if (bp)
  689. {
  690. // NOTE - not very efficient but that's not an issue here!
  691. CommonXmlWriter me(0,0);
  692. CommonXmlWriter him(0,0);
  693. toXML(&me);
  694. bp->toXML(&him);
  695. return strcmp(me.str(), him.str())==0;
  696. }
  697. else
  698. return false;
  699. }
  700. bool CBreakpointInfo::canMatchAny(IOutputMetaData *meta)
  701. {
  702. if (rowMatcher)
  703. return rowMatcher->canMatchAny(meta);
  704. else
  705. return true;
  706. }
  707. bool CBreakpointInfo::matches(const void *row, bool isEOF, unsigned edgeRowCount, IOutputMetaData *meta) const
  708. {
  709. // First check the conditions
  710. switch (condition)
  711. {
  712. case BreakpointConditionNone:
  713. break;
  714. case BreakpointConditionEOG:
  715. if (row)
  716. return false;
  717. break;
  718. case BreakpointConditionEOF:
  719. if (row || !(meta->isGrouped() || isEOF))
  720. return false;
  721. break;
  722. default:
  723. if (rowMatcher)
  724. {
  725. if (!row)
  726. return false;
  727. rowMatcher->reset();
  728. meta->toXML((const byte *) row, *rowMatcher);
  729. if (!rowMatcher->matched())
  730. return false; // expression failed
  731. }
  732. else
  733. throwUnexpected();
  734. }
  735. // ok, we've been hit.
  736. if (rowCount)
  737. {
  738. switch (rowCountMode)
  739. {
  740. case BreakpointCountEquals:
  741. return (edgeRowCount == rowCount);
  742. case BreakpointCountAtleast:
  743. return (edgeRowCount >= rowCount);
  744. default:
  745. throwUnexpected();
  746. }
  747. }
  748. else
  749. return true;
  750. }
  751. bool CBreakpointInfo::idMatch(BreakpointMode _mode, const char *_id) const
  752. {
  753. if (mode==_mode)
  754. {
  755. if (mode==BreakpointModeGraph && !id.length())
  756. return true;
  757. if (strcmp(id.get(), _id)==0)
  758. return true;
  759. // See if it matches up to the first .
  760. const char *dotpos = strchr(_id, '.');
  761. if (dotpos)
  762. {
  763. unsigned len = dotpos-_id;
  764. if (len == id.length() && strncmp(id.get(), _id, len)==0)
  765. return true;
  766. }
  767. }
  768. return false;
  769. }
  770. BreakpointMode CBreakpointInfo::queryMode() const
  771. {
  772. return mode;
  773. }
  774. BreakpointActionMode CBreakpointInfo::queryAction() const
  775. {
  776. return action;
  777. }
  778. void CBreakpointInfo::toXML(IXmlWriter *output) const
  779. {
  780. output->outputCString(BreakpointModes[mode], "@mode");
  781. if (id) output->outputCString(id, "@id");
  782. output->outputCString(BreakpointActionModes[action], "@action");
  783. if (condition != BreakpointConditionNone)
  784. {
  785. output->outputCString(BreakpointConditionModes[condition], "@condition");
  786. if (rowMatcher)
  787. {
  788. if (rowMatcher->queryFieldName()) output->outputCString(rowMatcher->queryFieldName(), "@fieldName");
  789. if (rowMatcher->queryValue()) output->outputCString(rowMatcher->queryValue(), "@value");
  790. output->outputBool(rowMatcher->queryCaseSensitive(), "@caseSensitive");
  791. }
  792. }
  793. if (rowCount)
  794. {
  795. output->outputInt(rowCount, "@rowCount");
  796. output->outputCString(BreakpointCountModes[rowCountMode], "@rowCountMode");
  797. }
  798. ForEachItemIn(edgeIdx, activeEdges)
  799. {
  800. IActivityDebugContext &edge = activeEdges.item(edgeIdx);
  801. output->outputBeginNested("edge", false);
  802. output->outputCString(edge.queryEdgeId(), "@edgeId");
  803. output->outputEndNested("edge");
  804. }
  805. }
  806. //=======================================================================================
  807. DebugActivityRecord::DebugActivityRecord (IActivityBase *_activity, unsigned _iteration, unsigned _channel, unsigned _sequence)
  808. : activity(_activity),iteration(_iteration),channel(_channel),sequence(_sequence)
  809. {
  810. localCycles = 0;
  811. totalCycles = 0;
  812. idText.append(activity->queryId());
  813. if (iteration || channel)
  814. idText.appendf(".%d", iteration);
  815. if (channel)
  816. idText.appendf("#%d", channel);
  817. }
  818. void DebugActivityRecord::outputId(IXmlWriter *output, const char *fieldName)
  819. {
  820. output->outputString(idText.length(), idText.str(), fieldName);
  821. }
  822. const char *DebugActivityRecord::queryIdString()
  823. {
  824. return idText;
  825. }
  826. void DebugActivityRecord::outputProperties(IXmlWriter *output)
  827. {
  828. if (properties)
  829. {
  830. Owned<IPropertyIterator> iterator = properties->getIterator();
  831. iterator->first();
  832. while (iterator->isValid())
  833. {
  834. const char *propName = iterator->getPropKey();
  835. const char *propValue = properties->queryProp(propName);
  836. output->outputBeginNested("att", false); output->outputCString(propName, "@name"); output->outputCString(propValue, "@value"); output->outputEndNested("att");
  837. iterator->next();
  838. }
  839. }
  840. if (localCycles)
  841. {
  842. output->outputBeginNested("att", false);
  843. output->outputCString("localTime", "@name");
  844. output->outputUInt((unsigned) (cycle_to_nanosec(localCycles)/1000), "@value");
  845. output->outputEndNested("att");
  846. }
  847. if (totalCycles)
  848. {
  849. output->outputBeginNested("att", false);
  850. output->outputCString("totalTime", "@name");
  851. output->outputUInt((unsigned) (cycle_to_nanosec(totalCycles)/1000), "@value");
  852. output->outputEndNested("att");
  853. }
  854. }
  855. void DebugActivityRecord::setProperty(const char *propName, const char *propValue, unsigned _sequence)
  856. {
  857. if (!properties)
  858. properties.setown(createProperties(false));
  859. properties->setProp(propName, propValue);
  860. sequence = _sequence;
  861. }
  862. void DebugActivityRecord::updateTimes(unsigned _sequence)
  863. {
  864. unsigned __int64 newTotal = activity->queryTotalCycles();
  865. unsigned __int64 newLocal = activity->queryLocalCycles();
  866. if (localCycles != newLocal || totalCycles != newTotal)
  867. {
  868. localCycles = newLocal;
  869. totalCycles = newTotal;
  870. sequence = _sequence;
  871. }
  872. }
  873. //=======================================================================================
  874. const char * CBaseDebugContext::queryStateString(DebugState state)
  875. {
  876. switch (state)
  877. {
  878. case DebugStateCreated: return "created";
  879. case DebugStateLoading: return "loading";
  880. case DebugStateRunning: return "running";
  881. case DebugStateEdge: return "edge";
  882. case DebugStateBreakpoint: return "breakpoint";
  883. case DebugStateReady: return "created";
  884. case DebugStateGraphCreate: return "graph create";
  885. case DebugStateGraphStart: return "graph start";
  886. case DebugStateGraphEnd: return "graph end";
  887. case DebugStateGraphAbort: return "graph abort";
  888. case DebugStateException: return "exception";
  889. case DebugStateFailed: return "failed";
  890. case DebugStateFinished: return "finished";
  891. case DebugStateUnloaded: return "unloaded";
  892. case DebugStateQuit: return "quit";
  893. case DebugStateDetached: return "detached";
  894. case DebugStateLimit: return "limit hit";
  895. case DebugStateGraphFinished: return "graph finished";
  896. default: throwUnexpected();
  897. }
  898. }
  899. bool CBaseDebugContext::_checkPendingBreakpoints(DebugState state, const char *graphName)
  900. {
  901. bool stop = false;
  902. ForEachItemIn(idx, breakpoints)
  903. {
  904. IBreakpointInfo &bp = breakpoints.item(idx);
  905. if (bp.queryMode()==BreakpointModeGraph)
  906. {
  907. stop = stop || bp.idMatch(BreakpointModeGraph, graphName);
  908. }
  909. else
  910. {
  911. if (state==DebugStateGraphStart)
  912. currentGraph->setBreakpoint(bp);
  913. }
  914. }
  915. return stop;
  916. }
  917. CBaseDebugContext::CBaseDebugContext(const IContextLogger &_logctx) : logctx(_logctx)
  918. {
  919. currentGraph = NULL;
  920. graphChangeSequence = 0;
  921. prevGraphChangeSequence = 0;
  922. pendingBreakpointsDone = false;
  923. sequence = 1;
  924. defaultHistoryCapacity = 10;
  925. executeSequentially = true;
  926. running = true;
  927. detached = false;
  928. watchState = WatchStateContinue;
  929. currentBreakpointUID = (unsigned) -1;
  930. debuggerActive = 0;
  931. currentState = DebugStateCreated;
  932. skipRequested = false;
  933. stopOnLimits = true;
  934. debugCyclesAdjust = 0;
  935. }
  936. unsigned __int64 CBaseDebugContext::getCyclesAdjustment() const
  937. {
  938. return debugCyclesAdjust;
  939. }
  940. void CBaseDebugContext::noteGraphChanged()
  941. {
  942. graphChangeSequence++;
  943. }
  944. IGlobalEdgeRecord *CBaseDebugContext::getEdgeRecord(const char *edgeId)
  945. {
  946. Linked<IGlobalEdgeRecord> edgeRecord = globalCounts.getValue(edgeId);
  947. if (!edgeRecord)
  948. {
  949. edgeRecord.setown(new DebugEdgeRecord(sequence));
  950. globalCounts.setValue(edgeId, edgeRecord);
  951. }
  952. return edgeRecord.getClear();
  953. }
  954. void CBaseDebugContext::addBreakpoint(IBreakpointInfo &bp)
  955. {
  956. if (currentGraph)
  957. currentGraph->setBreakpoint(bp);
  958. breakpoints.append(bp);
  959. }
  960. void CBaseDebugContext::removeBreakpoint(IBreakpointInfo &bp)
  961. {
  962. ForEachItemIn(idx, breakpoints)
  963. {
  964. if (breakpoints.item(idx).queryUID()==bp.queryUID())
  965. {
  966. if (currentGraph)
  967. currentGraph->removeBreakpoint(breakpoints.item(idx));
  968. breakpoints.remove(idx);
  969. break;
  970. }
  971. }
  972. }
  973. unsigned CBaseDebugContext::queryChannel() const
  974. {
  975. return 0;
  976. }
  977. void CBaseDebugContext::debugInitialize(const char *id, const char *_queryName, bool _breakAtStart)
  978. {
  979. throwUnexpected(); // Should only happen on server
  980. }
  981. void CBaseDebugContext::debugTerminate()
  982. {
  983. throwUnexpected(); // Should only happen on server
  984. }
  985. IBreakpointInfo *CBaseDebugContext::findBreakpoint(unsigned uid) const
  986. {
  987. ForEachItemIn(idx, breakpoints)
  988. {
  989. if (breakpoints.item(idx).queryUID()==uid)
  990. return &breakpoints.item(idx);
  991. }
  992. // we don't really expect to get here...
  993. throwUnexpected();
  994. return NULL;
  995. }
  996. BreakpointActionMode CBaseDebugContext::checkBreakpoint(DebugState state, IActivityDebugContext *probe, const void *extra)
  997. {
  998. if (detached)
  999. return BreakpointActionContinue;
  1000. class ActivityTimer
  1001. {
  1002. unsigned __int64 startCycles;
  1003. unsigned __int64 &accumulator;
  1004. public:
  1005. inline ActivityTimer(unsigned __int64 &_accumulator) : accumulator(_accumulator)
  1006. {
  1007. startCycles = get_cycles_now();
  1008. }
  1009. inline ~ActivityTimer()
  1010. {
  1011. unsigned __int64 elapsed = get_cycles_now() - startCycles;
  1012. accumulator += elapsed;
  1013. }
  1014. } timer(debugCyclesAdjust);
  1015. CriticalBlock b(breakCrit); // Note - this may block for a while if program is suspended!
  1016. assertex(running);
  1017. bool stop = false;
  1018. currentNode.clear();
  1019. if (watchState == WatchStateQuit)
  1020. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Query aborted by debugger");
  1021. if (state==DebugStateEdge)
  1022. {
  1023. // Check whether there is a breakpoint etc active for this activityId
  1024. IBreakpointInfo *bp;
  1025. if (currentBreakpointUID != (unsigned) -1)
  1026. bp = findBreakpoint(currentBreakpointUID);
  1027. else if (probe)
  1028. bp = probe->debuggerCallback(sequence, extra);
  1029. else
  1030. bp = NULL;
  1031. if (bp)
  1032. {
  1033. BreakpointActionMode action = bp->queryAction();
  1034. if (action != BreakpointActionBreak)
  1035. return action;
  1036. stop = true;
  1037. currentBreakpointUID = bp->queryUID();
  1038. }
  1039. else
  1040. {
  1041. switch (watchState)
  1042. {
  1043. case WatchStateStep:
  1044. stop = true;
  1045. break;
  1046. case WatchStateNext:
  1047. stop = (probe==nextActivity); // MORE - proxy activities need to be compared more carefully
  1048. break;
  1049. case WatchStateOver:
  1050. stop = (probe->queryInputActivity()==nextActivity); // MORE - doesn't work where multiple inputs or when debugging slave graph - think about proxy too
  1051. break;
  1052. case WatchStateGraph:
  1053. case WatchStateContinue:
  1054. stop = false;
  1055. break;
  1056. default:
  1057. throwUnexpected();
  1058. break;
  1059. }
  1060. }
  1061. }
  1062. else if (state==DebugStateGraphCreate)
  1063. pendingBreakpointsDone = false;
  1064. else if (state==DebugStateGraphStart || state==DebugStateGraphEnd || state==DebugStateGraphAbort)
  1065. {
  1066. stop = _checkPendingBreakpoints(state, (const char *) extra);
  1067. pendingBreakpointsDone = true;
  1068. switch (watchState)
  1069. {
  1070. case WatchStateGraph:
  1071. stop = true;
  1072. break;
  1073. case WatchStateStep:
  1074. case WatchStateNext:
  1075. stop = true;
  1076. break;
  1077. case WatchStateContinue:
  1078. // stop already set above...
  1079. break;
  1080. default:
  1081. throwUnexpected();
  1082. break;
  1083. }
  1084. #if 0
  1085. // I don't think this works, and the global count stuff will supercede it
  1086. if (state==DebugStateGraphEnd || state==DebugStateGraphAbort)
  1087. {
  1088. CommonXmlWriter finalGraphXML(0, 1);
  1089. currentGraph->getXGMML(&finalGraphXML, 0, false);
  1090. if (!completedGraphs)
  1091. completedGraphs.setown(createProperties(false));
  1092. completedGraphs->setProp(currentGraph->queryGraphName(), finalGraphXML.str());
  1093. }
  1094. #endif
  1095. }
  1096. else if (state==DebugStateFinished)
  1097. {
  1098. currentGraph = NULL;
  1099. graphChangeSequence++;
  1100. stop = true;
  1101. }
  1102. else if (state==DebugStateException)
  1103. {
  1104. IException *E = (IException *) extra;
  1105. if (E != currentException)
  1106. {
  1107. currentException.set(E);
  1108. stop = true;
  1109. }
  1110. else
  1111. stop = false;
  1112. }
  1113. else if (state==DebugStateLimit)
  1114. {
  1115. if (stopOnLimits)
  1116. {
  1117. stop = true;
  1118. IActivityBase *activity = (IActivityBase *) extra;
  1119. assertex(currentGraph);
  1120. currentNode.setown(currentGraph->getNodeByActivityBase(activity));
  1121. }
  1122. }
  1123. else
  1124. stop = true;
  1125. if (!stop)
  1126. return BreakpointActionContinue;
  1127. {
  1128. CriticalBlock b(debugCrit);
  1129. currentActivity.set(probe);
  1130. running = false;
  1131. currentState = state;
  1132. if (currentActivity)
  1133. logctx.CTXLOG("DEBUG: paused waiting for debugger, state %s, edge %s", queryStateString(state), currentActivity->queryEdgeId());
  1134. else
  1135. logctx.CTXLOG("DEBUG: paused waiting for debugger, state %s, edge '(none)'", queryStateString(state));
  1136. if (debuggerActive) // Was the debugger actively waiting for the program to hit a breakpoint?
  1137. {
  1138. debuggerSem.signal(debuggerActive);
  1139. debuggerActive = 0;
  1140. }
  1141. }
  1142. waitForDebugger(state, probe);
  1143. {
  1144. CriticalBlock b(debugCrit);
  1145. running = true;
  1146. BreakpointActionMode ret = skipRequested ? BreakpointActionSkip : BreakpointActionContinue;
  1147. skipRequested = false;
  1148. currentBreakpointUID = (unsigned) -1;
  1149. return ret;
  1150. }
  1151. }
  1152. void CBaseDebugContext::noteManager(IDebugGraphManager *mgr)
  1153. {
  1154. currentGraph = mgr;
  1155. graphChangeSequence++;
  1156. }
  1157. void CBaseDebugContext::releaseManager(IDebugGraphManager *mgr)
  1158. {
  1159. if(currentGraph==mgr)
  1160. currentGraph = NULL;
  1161. graphChangeSequence++;
  1162. }
  1163. unsigned CBaseDebugContext::querySequence()
  1164. {
  1165. return ++sequence;
  1166. }
  1167. unsigned CBaseDebugContext::getDefaultHistoryCapacity() const
  1168. {
  1169. return defaultHistoryCapacity;
  1170. }
  1171. bool CBaseDebugContext::getExecuteSequentially() const
  1172. {
  1173. return executeSequentially;
  1174. }
  1175. void CBaseDebugContext::checkDelayedBreakpoints(IActivityDebugContext *edge)
  1176. {
  1177. if (pendingBreakpointsDone) // Don't bother if the graph is still being created.... we can do them all more efficiently.
  1178. {
  1179. // This code is for edges created AFTER the bulk of the graph is created, which missed out on the normal delayed breakpoint setting...
  1180. graphChangeSequence++;
  1181. ForEachItemIn(bpIdx, breakpoints)
  1182. {
  1183. IBreakpointInfo &breakpoint = breakpoints.item(bpIdx);
  1184. if (breakpoint.idMatch(BreakpointModeEdge, edge->queryEdgeId()))
  1185. edge->setBreakpoint(breakpoint);
  1186. }
  1187. }
  1188. }
  1189. void CBaseDebugContext::serialize(MemoryBuffer &buff) const
  1190. {
  1191. buff.append((unsigned short) breakpoints.length());
  1192. ForEachItemIn(idx, breakpoints)
  1193. {
  1194. breakpoints.item(idx).serialize(buff);
  1195. }
  1196. buff.append(running).append(detached).append((unsigned char) watchState).append(sequence).append(skipRequested).append(stopOnLimits);
  1197. }
  1198. void CBaseDebugContext::deserialize(MemoryBuffer &buff)
  1199. {
  1200. // MORE - this is rather inefficient - we remove all breakpoints then reapply. But may be good enough...
  1201. if (currentGraph)
  1202. {
  1203. ForEachItemIn(idx, breakpoints)
  1204. {
  1205. IBreakpointInfo &bp = breakpoints.item(idx);
  1206. currentGraph->removeBreakpoint(bp);
  1207. }
  1208. }
  1209. breakpoints.kill();
  1210. unsigned short numBreaks;
  1211. buff.read(numBreaks);
  1212. while (numBreaks--)
  1213. {
  1214. IBreakpointInfo &bp = *new CBreakpointInfo(buff);
  1215. breakpoints.append(bp);
  1216. if (currentGraph)
  1217. currentGraph->setBreakpoint(bp);
  1218. }
  1219. pendingBreakpointsDone = true;
  1220. unsigned char watchStateChar;
  1221. buff.read(running).read(detached).read(watchStateChar).read(sequence).read(skipRequested).read(stopOnLimits);
  1222. watchState = (WatchState) watchStateChar;
  1223. }
  1224. //=======================================================================================
  1225. void CBaseServerDebugContext::doStandardResult(IXmlWriter *output) const
  1226. {
  1227. const char *stateString = running ? "running" : queryStateString(currentState);
  1228. output->outputInt(sequence, "@sequence");
  1229. output->outputString(strlen(stateString), stateString, "@state");
  1230. if (currentNode)
  1231. currentNode->outputId(output, "@nodeId");
  1232. if (currentActivity)
  1233. output->outputCString(currentActivity->queryEdgeId(), "@edgeId");
  1234. if (skipRequested)
  1235. output->outputBool(true, "@skip");
  1236. if (currentGraph)
  1237. output->outputCString(currentGraph->queryGraphName(), "@graphId");
  1238. output->outputInt(graphChangeSequence, "@graphSequenceNum");
  1239. if (graphChangeSequence != prevGraphChangeSequence)
  1240. output->outputBool(true, "@graphChanged");
  1241. if (currentBreakpointUID != (unsigned) -1)
  1242. {
  1243. IBreakpointInfo *bp = findBreakpoint(currentBreakpointUID);
  1244. _listBreakpoint(output, *bp, breakpoints.find(*bp));
  1245. }
  1246. if (currentException)
  1247. {
  1248. output->outputBeginNested("Exception", true);
  1249. output->outputCString("Roxie", "Source");
  1250. output->outputInt(currentException->errorCode(), "Code");
  1251. StringBuffer s;
  1252. currentException->errorMessage(s);
  1253. output->outputString(s.length(), s.str(), "Message");
  1254. output->outputEndNested("Exception");
  1255. }
  1256. }
  1257. void CBaseServerDebugContext::_listBreakpoint(IXmlWriter *output, IBreakpointInfo &bp, unsigned idx) const
  1258. {
  1259. if (bp.queryMode() != BreakpointModeNone)
  1260. {
  1261. output->outputBeginNested("break", true);
  1262. output->outputInt(idx, "@idx");
  1263. bp.toXML(output);
  1264. output->outputEndNested("break");
  1265. }
  1266. }
  1267. void CBaseServerDebugContext::_continue(WatchState watch)
  1268. {
  1269. if (running)
  1270. throw MakeStringException(THORHELPER_INTERNAL_ERROR, "Query already running");
  1271. if (watch==WatchStateNext || watch==WatchStateOver)
  1272. {
  1273. if (!currentActivity)
  1274. throw MakeStringException(THORHELPER_INTERNAL_ERROR, "next: no current activity");
  1275. nextActivity.set(currentActivity);
  1276. }
  1277. else
  1278. nextActivity.clear();
  1279. watchState = watch;
  1280. previousSequence = sequence;
  1281. prevGraphChangeSequence = graphChangeSequence;
  1282. debuggerActive++;
  1283. debugeeSem.signal();
  1284. {
  1285. CriticalUnblock b(debugCrit);
  1286. debuggerSem.wait(); // MORE - is that actually correct, to release the crit while waiting? Think...
  1287. }
  1288. }
  1289. unsigned CBaseServerDebugContext::checkOption(const char *supplied, const char *name, const char *accepted[])
  1290. {
  1291. if (supplied)
  1292. {
  1293. unsigned idx = 0;
  1294. while (accepted[idx])
  1295. {
  1296. if (strcmp(accepted[idx], supplied)==0)
  1297. return idx;
  1298. idx++;
  1299. }
  1300. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Invalid parameter value %s='%s'", name, supplied);
  1301. }
  1302. else
  1303. return 0; // first is default
  1304. }
  1305. IBreakpointInfo *CBaseServerDebugContext::createDummyBreakpoint()
  1306. {
  1307. return new CBreakpointInfo(BreakpointModeNone);
  1308. }
  1309. IBreakpointInfo *CBaseServerDebugContext::_createBreakpoint(const char *modeString, const char *id, const char *action,
  1310. const char *fieldName, const char *condition, const char *value, bool caseSensitive,
  1311. unsigned hitCount, const char *hitCountMode)
  1312. {
  1313. BreakpointMode mode = (BreakpointMode) checkOption(modeString, "mode", BreakpointModes);
  1314. if (!id)
  1315. {
  1316. if (mode==BreakpointModeEdge)
  1317. {
  1318. if (!currentActivity)
  1319. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No current activity");
  1320. id = currentActivity->queryEdgeId();
  1321. }
  1322. else if (mode==BreakpointModeNode)
  1323. {
  1324. if (!currentActivity)
  1325. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No current activity");
  1326. id = currentActivity->querySourceId();
  1327. }
  1328. }
  1329. return new CBreakpointInfo(mode, id, (BreakpointActionMode) checkOption(action, "action", BreakpointActionModes),
  1330. fieldName, (BreakpointConditionMode) checkOption(condition, "condition", BreakpointConditionModes), value, caseSensitive,
  1331. hitCount, (BreakpointCountMode) checkOption(hitCountMode, "hitCountMode", BreakpointCountModes));
  1332. }
  1333. void CBaseServerDebugContext::waitForDebugger(DebugState state, IActivityDebugContext *probe)
  1334. {
  1335. sequence++;
  1336. while (!debugeeSem.wait(DEBUGEE_TIMEOUT))
  1337. {
  1338. if (onDebuggerTimeout())
  1339. break;
  1340. }
  1341. sequence++;
  1342. }
  1343. CBaseServerDebugContext::CBaseServerDebugContext(const IContextLogger &_logctx, IPropertyTree *_queryXGMML, SafeSocket &_client)
  1344. : CBaseDebugContext(_logctx), queryXGMML(_queryXGMML), client(_client)
  1345. {
  1346. sequence = 0;
  1347. previousSequence = 0;
  1348. breakpoints.append(*createDummyBreakpoint()); // temporary breakpoint always present
  1349. }
  1350. void CBaseServerDebugContext::serializeBreakpoints(MemoryBuffer &to)
  1351. {
  1352. to.append(breakpoints.length());
  1353. ForEachItemIn(idx, breakpoints)
  1354. breakpoints.item(idx).serialize(to);
  1355. }
  1356. void CBaseServerDebugContext::debugInitialize(const char *id, const char *_queryName, bool _breakAtStart)
  1357. {
  1358. if (!_breakAtStart)
  1359. detached = true;
  1360. debugId.set(id);
  1361. queryName.set(_queryName);
  1362. currentState = DebugStateLoading;
  1363. }
  1364. void CBaseServerDebugContext::debugTerminate()
  1365. {
  1366. CriticalBlock b(debugCrit);
  1367. assertex(running);
  1368. currentState = DebugStateUnloaded;
  1369. running = false;
  1370. if (debuggerActive)
  1371. {
  1372. debuggerSem.signal(debuggerActive);
  1373. debuggerActive = 0;
  1374. }
  1375. }
  1376. void CBaseServerDebugContext::addBreakpoint(IXmlWriter *output, const char *modeString, const char *id, const char *action,
  1377. const char *fieldName, const char *condition, const char *value, bool caseSensitive,
  1378. unsigned hitCount, const char *hitCountMode)
  1379. {
  1380. CriticalBlock b(debugCrit);
  1381. if (running)
  1382. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1383. Owned<IBreakpointInfo> newBreakpoint = _createBreakpoint(modeString, id, action, fieldName, condition, value, caseSensitive, hitCount, hitCountMode);
  1384. // For breakpoints to be efficient we need to set them in the probe, though because the activity probe may not exist yet, we should also
  1385. // keep a central list (also makes it easier to do things like list breakpoint info...)
  1386. ForEachItemIn(idx, breakpoints)
  1387. {
  1388. IBreakpointInfo &bp = breakpoints.item(idx);
  1389. if (bp.equals(*newBreakpoint))
  1390. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Breakpoint already exists");
  1391. }
  1392. _listBreakpoint(output, *newBreakpoint, breakpoints.ordinality());
  1393. CBaseDebugContext::addBreakpoint(*newBreakpoint.getClear());
  1394. }
  1395. void CBaseServerDebugContext::removeBreakpoint(IXmlWriter *output, unsigned removeIdx)
  1396. {
  1397. CriticalBlock b(debugCrit);
  1398. if (running)
  1399. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1400. assertex(removeIdx);
  1401. if (!breakpoints.isItem(removeIdx))
  1402. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Breakpoint %d does not exist", removeIdx);
  1403. IBreakpointInfo &bp = breakpoints.item(removeIdx);
  1404. _listBreakpoint(output, bp, removeIdx);
  1405. if (currentGraph)
  1406. currentGraph->removeBreakpoint(bp);
  1407. breakpoints.replace(*createDummyBreakpoint(), removeIdx);
  1408. }
  1409. void CBaseServerDebugContext::removeAllBreakpoints(IXmlWriter *output)
  1410. {
  1411. ForEachItemIn(idx, breakpoints)
  1412. {
  1413. IBreakpointInfo &bp = breakpoints.item(idx);
  1414. _listBreakpoint(output, bp, idx);
  1415. if (currentGraph)
  1416. currentGraph->removeBreakpoint(bp);
  1417. breakpoints.replace(*createDummyBreakpoint(), idx);
  1418. }
  1419. }
  1420. void CBaseServerDebugContext::listBreakpoint(IXmlWriter *output, unsigned listIdx) const
  1421. {
  1422. // MORE - fair amount could be commoned up with remove...
  1423. CriticalBlock b(debugCrit);
  1424. if (running)
  1425. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1426. assertex(listIdx);
  1427. if (!breakpoints.isItem(listIdx))
  1428. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Breakpoint %d does not exist", listIdx);
  1429. IBreakpointInfo &bp = breakpoints.item(listIdx);
  1430. _listBreakpoint(output, bp, listIdx);
  1431. }
  1432. void CBaseServerDebugContext::listAllBreakpoints(IXmlWriter *output) const
  1433. {
  1434. ForEachItemIn(idx, breakpoints)
  1435. {
  1436. IBreakpointInfo &bp = breakpoints.item(idx);
  1437. _listBreakpoint(output, bp, idx);
  1438. }
  1439. }
  1440. void CBaseServerDebugContext::debugInterrupt(IXmlWriter *output)
  1441. {
  1442. CriticalBlock b(debugCrit);
  1443. if (!running)
  1444. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Query is already paused"); // MORE - or has terminated?
  1445. detached = false;
  1446. nextActivity.clear();
  1447. watchState = WatchStateStep;
  1448. debuggerActive++;
  1449. {
  1450. CriticalUnblock b(debugCrit);
  1451. debuggerSem.wait(); // MORE - should this be inside critsec? Should it be there at all?
  1452. }
  1453. doStandardResult(output);
  1454. }
  1455. void CBaseServerDebugContext::debugContinue(IXmlWriter *output, const char *modeString, const char *id)
  1456. {
  1457. CriticalBlock b(debugCrit);
  1458. Owned<IBreakpointInfo> tempBreakpoint;
  1459. if (id)
  1460. {
  1461. tempBreakpoint.setown(_createBreakpoint(modeString, id));
  1462. if (currentGraph)
  1463. currentGraph->setBreakpoint(*tempBreakpoint);
  1464. breakpoints.replace(*LINK(tempBreakpoint), 0);
  1465. }
  1466. _continue(WatchStateContinue);
  1467. doStandardResult(output);
  1468. if (tempBreakpoint)
  1469. {
  1470. if (currentGraph)
  1471. currentGraph->removeBreakpoint(*tempBreakpoint);
  1472. breakpoints.replace(*createDummyBreakpoint(), 0);
  1473. }
  1474. }
  1475. void CBaseServerDebugContext::debugRun(IXmlWriter *output)
  1476. {
  1477. CriticalBlock b(debugCrit);
  1478. if (running)
  1479. throw MakeStringException(THORHELPER_INTERNAL_ERROR, "Query already running");
  1480. watchState = WatchStateContinue;
  1481. detached = true;
  1482. if (currentGraph)
  1483. currentGraph->clearHistories();
  1484. currentState = DebugStateDetached;
  1485. currentActivity.clear();
  1486. currentBreakpointUID = (unsigned) -1;
  1487. previousSequence = sequence;
  1488. debugeeSem.signal();
  1489. doStandardResult(output);
  1490. }
  1491. void CBaseServerDebugContext::debugQuit(IXmlWriter *output)
  1492. {
  1493. CriticalBlock b(debugCrit);
  1494. detached = false;
  1495. nextActivity.clear();
  1496. watchState = WatchStateQuit;
  1497. currentState = DebugStateQuit;
  1498. currentActivity.clear();
  1499. currentBreakpointUID = (unsigned) -1;
  1500. if (!running)
  1501. {
  1502. debugeeSem.signal();
  1503. }
  1504. doStandardResult(output);
  1505. }
  1506. void CBaseServerDebugContext::debugSkip(IXmlWriter *output)
  1507. {
  1508. CriticalBlock b(debugCrit);
  1509. if (running)
  1510. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1511. if (!currentActivity)
  1512. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No current activity");
  1513. skipRequested = true;
  1514. doStandardResult(output);
  1515. }
  1516. void CBaseServerDebugContext::debugStatus(IXmlWriter *output) const
  1517. {
  1518. CriticalBlock b(debugCrit);
  1519. doStandardResult(output);
  1520. }
  1521. void CBaseServerDebugContext::debugStep(IXmlWriter *output, const char *modeString)
  1522. {
  1523. CriticalBlock b(debugCrit);
  1524. WatchState state;
  1525. if (modeString)
  1526. {
  1527. if (strcmp(modeString, "graph")==0)
  1528. state = WatchStateGraph;
  1529. else if (strcmp(modeString, "edge")==0)
  1530. state = WatchStateStep;
  1531. else
  1532. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Step mode should be edge or graph");
  1533. }
  1534. else
  1535. state = WatchStateStep;
  1536. _continue(state);
  1537. doStandardResult(output);
  1538. }
  1539. void CBaseServerDebugContext::debugNext(IXmlWriter *output)
  1540. {
  1541. CriticalBlock b(debugCrit);
  1542. _continue(WatchStateNext);
  1543. doStandardResult(output);
  1544. }
  1545. void CBaseServerDebugContext::debugOver(IXmlWriter *output)
  1546. {
  1547. CriticalBlock b(debugCrit);
  1548. _continue(WatchStateOver);
  1549. doStandardResult(output);
  1550. }
  1551. void CBaseServerDebugContext::debugChanges(IXmlWriter *output, unsigned sinceSequence) const
  1552. {
  1553. CriticalBlock b(debugCrit);
  1554. if (running)
  1555. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1556. if (sinceSequence == (unsigned) -1)
  1557. sinceSequence = previousSequence;
  1558. if (!currentGraph)
  1559. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available when no graph active");
  1560. // MORE - if current graph has changed since specified sequence, then ??
  1561. currentGraph->getXGMML(output, sinceSequence, true);
  1562. }
  1563. void CBaseServerDebugContext::debugCounts(IXmlWriter *output, unsigned sinceSequence, bool reset)
  1564. {
  1565. CriticalBlock b(debugCrit);
  1566. if (running)
  1567. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1568. if (sinceSequence == (unsigned) -1)
  1569. sinceSequence = previousSequence;
  1570. HashIterator edges(globalCounts);
  1571. ForEach(edges)
  1572. {
  1573. IGlobalEdgeRecord *edge = globalCounts.mapToValue(&edges.query());
  1574. if (!sinceSequence || edge->queryLastSequence() > sinceSequence)
  1575. {
  1576. output->outputBeginNested("edge", true);
  1577. output->outputCString((const char *) edges.query().getKey(), "@edgeId");
  1578. output->outputUInt(edge->queryCount(), "@count");
  1579. output->outputEndNested("edge");
  1580. }
  1581. if (reset)
  1582. edge->reset();
  1583. }
  1584. }
  1585. void CBaseServerDebugContext::debugWhere(IXmlWriter *output) const
  1586. {
  1587. CriticalBlock b(debugCrit);
  1588. if (running)
  1589. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1590. if (!currentActivity)
  1591. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No current activity");
  1592. output->outputCString(currentGraph->queryGraphName(), "@graphId");
  1593. output->outputCString(currentActivity->queryEdgeId(), "@edgeId");
  1594. IActivityDebugContext *activityCtx = currentActivity;
  1595. while (activityCtx)
  1596. {
  1597. activityCtx->printEdge(output, 0, 1);
  1598. output->outputEndNested("Edge");
  1599. activityCtx = activityCtx->queryInputActivity();
  1600. }
  1601. }
  1602. void CBaseServerDebugContext::debugPrintVariable(IXmlWriter *output, const char *name, const char *type) const
  1603. {
  1604. throwUnexpected(); // must be implemented by platform-specific derived class
  1605. }
  1606. void CBaseServerDebugContext::debugSearch(IXmlWriter *output, const char *fieldName, const char *condition, const char *value, bool caseSensitive, bool fullRows) const
  1607. {
  1608. CriticalBlock b(debugCrit);
  1609. if (running)
  1610. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1611. if (!currentGraph)
  1612. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available when no graph active");
  1613. Owned<IRowMatcher> searcher = createRowMatcher(fieldName, (BreakpointConditionMode) checkOption(condition, "condition", BreakpointConditionModes), value, caseSensitive);
  1614. currentGraph->searchHistories(output, searcher, fullRows);
  1615. }
  1616. void CBaseServerDebugContext::debugPrint(IXmlWriter *output, const char *edgeId, unsigned startRow, unsigned numRows) const
  1617. {
  1618. CriticalBlock b(debugCrit);
  1619. if (running)
  1620. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1621. if (!currentGraph)
  1622. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available when no graph active");
  1623. IActivityDebugContext *activityCtx;
  1624. if (edgeId)
  1625. {
  1626. activityCtx = currentGraph->lookupActivityByEdgeId(edgeId);
  1627. if (!activityCtx)
  1628. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Edge %s not found in current graph", edgeId);
  1629. }
  1630. else if (currentActivity)
  1631. activityCtx = currentActivity;
  1632. else
  1633. throw MakeStringException(THORHELPER_DEBUG_ERROR, "An edge id must be specified if there is no current edge");
  1634. output->outputCString(currentGraph->queryGraphName(), "@graphId");
  1635. #if 1
  1636. CommonXmlWriter dummyOutput(0, 0);
  1637. activityCtx->printEdge(&dummyOutput, startRow, numRows); // MORE - need to suppress the <Edge> if we want backward compatibility!
  1638. Owned<IPropertyTree> result = createPTreeFromXMLString(dummyOutput.str());
  1639. if (result)
  1640. {
  1641. Owned<IAttributeIterator> attributes = result->getAttributes();
  1642. ForEach(*attributes)
  1643. {
  1644. output->outputCString(attributes->queryValue(),attributes->queryName());
  1645. }
  1646. output->outputString(0, NULL, NULL);
  1647. Owned<IPropertyTreeIterator> elems = result->getElements("*");
  1648. ForEach(*elems)
  1649. {
  1650. StringBuffer e;
  1651. IPropertyTree &elem = elems->query();
  1652. toXML(&elem, e);
  1653. output->outputQuoted(e.str());
  1654. }
  1655. }
  1656. #else
  1657. activityCtx->printEdge(output, startRow, numRows); // MORE - need to suppress the <Edge> if we want backward compatibility!
  1658. #endif
  1659. }
  1660. void CBaseServerDebugContext::debugGetConfig(IXmlWriter *output, const char *name, const char *id) const
  1661. {
  1662. CriticalBlock b(debugCrit);
  1663. if (running)
  1664. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1665. if (!name)
  1666. throw MakeStringException(THORHELPER_DEBUG_ERROR, "configuration setting name must be supplied");
  1667. if (stricmp(name, "executeSequentially")==0)
  1668. {
  1669. output->outputBool(executeSequentially, "@value");
  1670. }
  1671. else if (stricmp(name, "stopOnLimits")==0)
  1672. {
  1673. output->outputInt(stopOnLimits, "@value");
  1674. }
  1675. else if (stricmp(name, "historySize")==0)
  1676. {
  1677. if (id)
  1678. {
  1679. if (!currentGraph)
  1680. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available when no graph active");
  1681. IActivityDebugContext *activityCtx = currentGraph->lookupActivityByEdgeId(id);
  1682. if (!activityCtx)
  1683. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Edge %s not found in current graph", id);
  1684. output->outputInt(activityCtx->queryHistoryCapacity(), "@value");
  1685. }
  1686. else
  1687. {
  1688. output->outputInt(defaultHistoryCapacity, "@value");
  1689. }
  1690. }
  1691. else
  1692. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Unknown configuration setting '%s'", name);
  1693. output->outputCString(name, "@name");
  1694. if (id) output->outputCString(id, "@id");
  1695. }
  1696. void CBaseServerDebugContext::debugSetConfig(IXmlWriter *output, const char *name, const char *value, const char *id)
  1697. {
  1698. CriticalBlock b(debugCrit);
  1699. if (running)
  1700. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1701. if (!name)
  1702. throw MakeStringException(THORHELPER_DEBUG_ERROR, "configuration setting name must be supplied");
  1703. unsigned intval = value ? atoi(value) : 0;
  1704. if (stricmp(name, "executeSequentially")==0)
  1705. {
  1706. if (id)
  1707. throw MakeStringException(THORHELPER_DEBUG_ERROR, "id not supported here");
  1708. if (!value || clipStrToBool(value))
  1709. executeSequentially = true;
  1710. else
  1711. executeSequentially = false;
  1712. }
  1713. else if (stricmp(name, "stopOnLimits")==0)
  1714. {
  1715. if (id)
  1716. throw MakeStringException(THORHELPER_DEBUG_ERROR, "id not supported here");
  1717. if (!value || clipStrToBool(value))
  1718. stopOnLimits = true;
  1719. else
  1720. stopOnLimits = false;
  1721. }
  1722. else if (stricmp(name, "historySize")==0)
  1723. {
  1724. if (!value)
  1725. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No value supplied");
  1726. if (id)
  1727. {
  1728. if (!currentGraph)
  1729. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available when no graph active");
  1730. IActivityDebugContext *activityCtx = currentGraph->lookupActivityByEdgeId(id);
  1731. if (!activityCtx)
  1732. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Edge %s not found in current graph", id);
  1733. activityCtx->setHistoryCapacity(intval);
  1734. }
  1735. else
  1736. {
  1737. defaultHistoryCapacity = intval;
  1738. if (currentGraph)
  1739. currentGraph->setHistoryCapacity(intval);
  1740. }
  1741. }
  1742. else
  1743. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Unknown configuration setting '%s'", name);
  1744. if (name) output->outputCString(name, "@name");
  1745. if (value) output->outputCString(value, "@value");
  1746. if (id) output->outputCString(id, "@id");
  1747. }
  1748. void CBaseServerDebugContext::getCurrentGraphXGMML(IXmlWriter *output, bool original) const
  1749. {
  1750. CriticalBlock b(debugCrit);
  1751. if (running)
  1752. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1753. if (!currentGraph)
  1754. throw MakeStringException(THORHELPER_DEBUG_ERROR, "No current graph");
  1755. if (original)
  1756. getGraphXGMML(output, currentGraph->queryGraphName());
  1757. else
  1758. {
  1759. #if 0
  1760. if (completedGraphs)
  1761. {
  1762. Owned<IPropertyIterator> iterator = completedGraphs->getIterator();
  1763. iterator->first();
  1764. while (iterator->isValid())
  1765. {
  1766. const char *graphName = iterator->getPropKey();
  1767. const char *graphXML = completedGraphs->queryProp(graphName);
  1768. output->outputString(0, 0, NULL);
  1769. output->outputQuoted(graphXML);
  1770. iterator->next();
  1771. }
  1772. }
  1773. #endif
  1774. currentGraph->getXGMML(output, 0, true);
  1775. }
  1776. }
  1777. void CBaseServerDebugContext::getQueryXGMML(IXmlWriter *output) const
  1778. {
  1779. CriticalBlock b(debugCrit);
  1780. if (running)
  1781. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1782. StringBuffer s;
  1783. toXML(queryXGMML, s, 2);
  1784. output->outputString(0, NULL, NULL); // closes any open tags....
  1785. output->outputQuoted(s.str());
  1786. }
  1787. void CBaseServerDebugContext::getGraphXGMML(IXmlWriter *output, const char *graphName) const
  1788. {
  1789. CriticalBlock b(debugCrit);
  1790. if (running)
  1791. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Command not available while query is running");
  1792. StringBuffer xpath;
  1793. xpath.appendf("Graph[@id='%s']", graphName);
  1794. Owned<IPropertyTree> graph = queryXGMML->getPropTree(xpath.str());
  1795. if (!graph)
  1796. throw MakeStringException(THORHELPER_DEBUG_ERROR, "Graph %s not found", graphName);
  1797. StringBuffer s;
  1798. toXML(graph, s, 2);
  1799. output->outputString(0, NULL, NULL); // closes any open tags....
  1800. output->outputQuoted(s.str());
  1801. }
  1802. const char *CBaseServerDebugContext::queryQueryName() const
  1803. {
  1804. return queryName.get();
  1805. }
  1806. const char *CBaseServerDebugContext::queryDebugId() const
  1807. {
  1808. return debugId.get();
  1809. }
  1810. //=======================================================================================
  1811. DebugActivityRecord *CBaseDebugGraphManager::noteActivity(IActivityBase *activity, unsigned iteration, unsigned channel, unsigned sequence)
  1812. {
  1813. Linked<DebugActivityRecord> node = allActivities.getValue(activity);
  1814. if (!node)
  1815. {
  1816. node.setown(new DebugActivityRecord(activity, iteration, channel, sequence));
  1817. allActivities.setValue(activity, node);
  1818. }
  1819. else
  1820. {
  1821. node->iteration = iteration;
  1822. node->channel = channel;
  1823. node->sequence = sequence;
  1824. }
  1825. return node; // note - does not link
  1826. }
  1827. IDebuggableContext *CBaseDebugGraphManager::queryContext() const
  1828. {
  1829. return debugContext;
  1830. }
  1831. DebugActivityRecord *CBaseDebugGraphManager::getNodeByActivityBase(IActivityBase *activity) const
  1832. {
  1833. Linked<DebugActivityRecord> node = allActivities.getValue(activity);
  1834. if (!node)
  1835. {
  1836. ForEachItemIn(idx, childGraphs)
  1837. {
  1838. node.setown(childGraphs.item(idx).getNodeByActivityBase(activity));
  1839. if (node)
  1840. break;
  1841. }
  1842. }
  1843. return node.getClear();
  1844. }
  1845. void CBaseDebugGraphManager::outputLinksForChildGraph(IXmlWriter *output, const char *parentId)
  1846. {
  1847. ForEachItemIn(idx, sinks)
  1848. {
  1849. Linked<DebugActivityRecord> childNode = allActivities.getValue(&sinks.item(idx));
  1850. output->outputBeginNested("edge", true);
  1851. output->outputCString("Child", "@label");
  1852. StringBuffer idText;
  1853. idText.append(childNode->idText).append('_').append(parentId);
  1854. output->outputCString(idText.str(), "@id"); // MORE - is this guaranteed to be unique?
  1855. output->outputBeginNested("att", false); output->outputCString("_childGraph", "@name"); output->outputInt(1, "@value"); output->outputEndNested("att");
  1856. output->outputBeginNested("att", false); output->outputCString("_sourceActivity", "@name"); childNode->outputId(output, "@value"); output->outputEndNested("att"); // MORE!!!
  1857. output->outputBeginNested("att", false); output->outputCString("_targetActivity", "@name"); output->outputCString(parentId, "@value"); output->outputEndNested("att");
  1858. output->outputEndNested("edge");
  1859. }
  1860. }
  1861. void CBaseDebugGraphManager::outputChildGraph(IXmlWriter *output, unsigned sequence)
  1862. {
  1863. if (allActivities.count())
  1864. {
  1865. output->outputBeginNested("graph", true);
  1866. HashIterator edges(allProbes);
  1867. ForEach(edges)
  1868. {
  1869. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  1870. if (!sequence || edge->queryLastSequence() > sequence)
  1871. edge->getXGMML(output);
  1872. }
  1873. HashIterator nodes(allActivities);
  1874. ForEach(nodes)
  1875. {
  1876. DebugActivityRecord *node = allActivities.mapToValue(&nodes.query());
  1877. if (!sequence || node->sequence > sequence) // MORE - if we start reporting any stats on nodes (seeks? scans?) this may need to change
  1878. {
  1879. output->outputBeginNested("node", true);
  1880. node->outputId(output, "@id");
  1881. ThorActivityKind kind = node->activity->getKind();
  1882. StringBuffer kindStr(getActivityText(kind));
  1883. output->outputString(kindStr.length(), kindStr.str(), "@shortlabel");
  1884. if (node->activity->isPassThrough())
  1885. {
  1886. output->outputBeginNested("att", false); output->outputCString("_isPassthrough", "@name"); output->outputInt(1, "@value"); output->outputEndNested("att");
  1887. }
  1888. if (node->totalCycles)
  1889. {
  1890. output->outputBeginNested("att", false);
  1891. output->outputCString("totalTime", "@name");
  1892. output->outputUInt((unsigned) (cycle_to_nanosec(node->totalCycles)/1000), "@value");
  1893. output->outputEndNested("att");
  1894. }
  1895. if (node->localCycles)
  1896. {
  1897. output->outputBeginNested("att", false);
  1898. output->outputCString("localTime", "@name");
  1899. output->outputUInt((unsigned) (cycle_to_nanosec(node->localCycles)/1000), "@value");
  1900. output->outputEndNested("att");
  1901. }
  1902. node->outputProperties(output);
  1903. }
  1904. if (!sequence || node->sequence > sequence)
  1905. output->outputEndNested("node");
  1906. ForEachItemIn(idx, node->childGraphs)
  1907. {
  1908. output->outputBeginNested("node", true);
  1909. IDebugGraphManager &childGraph = node->childGraphs.item(idx);
  1910. output->outputCString(childGraph.queryIdString(), "@id");
  1911. childGraph.outputChildGraph(output, sequence);
  1912. output->outputEndNested("node");
  1913. childGraph.outputLinksForChildGraph(output, node->queryIdString());
  1914. }
  1915. }
  1916. output->outputEndNested("graph");
  1917. }
  1918. else
  1919. {
  1920. // This is a bit of a hack. Slave-side graphs have no outer-level node, so we have to do the childgraphs instead
  1921. ForEachItemIn(idx, childGraphs)
  1922. {
  1923. childGraphs.item(idx).outputChildGraph(output, sequence);
  1924. }
  1925. }
  1926. }
  1927. void CBaseDebugGraphManager::Link() const
  1928. {
  1929. CInterface::Link();
  1930. }
  1931. bool CBaseDebugGraphManager::Release() const
  1932. {
  1933. if (!IsShared())
  1934. {
  1935. if (!id)
  1936. debugContext->releaseManager(const_cast<CBaseDebugGraphManager*> (this));
  1937. }
  1938. return CInterface::Release();
  1939. }
  1940. CBaseDebugGraphManager::CBaseDebugGraphManager(IDebuggableContext *_debugContext, unsigned _id, const char *_graphName) : debugContext(_debugContext), id(_id), graphName(_graphName)
  1941. {
  1942. if (_graphName)
  1943. debugContext->noteManager(this);
  1944. idString.append(_id);
  1945. proxyId = 0;
  1946. }
  1947. IDebugGraphManager *CBaseDebugGraphManager::queryDebugManager()
  1948. {
  1949. return this;
  1950. }
  1951. const char *CBaseDebugGraphManager::queryIdString() const
  1952. {
  1953. return idString;
  1954. }
  1955. unsigned CBaseDebugGraphManager::queryId() const
  1956. {
  1957. return id;
  1958. }
  1959. void CBaseDebugGraphManager::noteSink(IActivityBase *sink)
  1960. {
  1961. CriticalBlock b(crit);
  1962. sinks.append(*LINK(sink));
  1963. }
  1964. void CBaseDebugGraphManager::noteDependency(IActivityBase *sourceActivity, unsigned sourceIndex, unsigned controlId, const char *edgeId, IActivityBase *targetActivity)
  1965. {
  1966. CriticalBlock b(crit);
  1967. dependencies.append(*new DebugDependencyRecord(sourceActivity, sourceIndex, controlId, edgeId, targetActivity, debugContext->querySequence()));
  1968. }
  1969. void CBaseDebugGraphManager::setNodeProperty(IActivityBase *node, const char *propName, const char *propValue)
  1970. {
  1971. CriticalBlock b(crit);
  1972. Linked<DebugActivityRecord> nodeInfo = allActivities.getValue(node);
  1973. if (nodeInfo)
  1974. nodeInfo->setProperty(propName, propValue, debugContext->querySequence());
  1975. ForEachItemIn(idx, childGraphs)
  1976. {
  1977. childGraphs.item(idx).setNodeProperty(node, propName, propValue);
  1978. }
  1979. }
  1980. void CBaseDebugGraphManager::setNodePropertyInt(IActivityBase *node, const char *propName, unsigned __int64 propValue)
  1981. {
  1982. StringBuffer s;
  1983. s.append(propValue);
  1984. setNodeProperty(node, propName, s.str());
  1985. }
  1986. void CBaseDebugGraphManager::getProbeResponse(IPropertyTree *query)
  1987. {
  1988. throwUnexpected();
  1989. }
  1990. void CBaseDebugGraphManager::setHistoryCapacity(unsigned newCapacity)
  1991. {
  1992. HashIterator edges(allProbes);
  1993. ForEach(edges)
  1994. {
  1995. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  1996. edge->setHistoryCapacity(newCapacity);
  1997. }
  1998. }
  1999. void CBaseDebugGraphManager::clearHistories()
  2000. {
  2001. HashIterator edges(allProbes);
  2002. ForEach(edges)
  2003. {
  2004. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  2005. edge->clearHistory();
  2006. }
  2007. }
  2008. void CBaseDebugGraphManager::setBreakpoint(IBreakpointInfo &bp)
  2009. {
  2010. // May want a hash lookup here, but the non-uniqueness makes it slightly non-trivial so bruteforce for now
  2011. HashIterator edges(allProbes);
  2012. ForEach(edges)
  2013. {
  2014. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  2015. if (bp.queryMode()==BreakpointModeGlobal || bp.idMatch(BreakpointModeEdge, edge->queryEdgeId()))
  2016. edge->setBreakpoint(bp);
  2017. }
  2018. ForEachItemIn(idx, childGraphs)
  2019. {
  2020. childGraphs.item(idx).setBreakpoint(bp);
  2021. }
  2022. }
  2023. void CBaseDebugGraphManager::mergeRemoteCounts(IDebuggableContext *into) const
  2024. {
  2025. ForEachItemIn(idx, childGraphs)
  2026. {
  2027. childGraphs.item(idx).mergeRemoteCounts(into);
  2028. }
  2029. }
  2030. void CBaseDebugGraphManager::removeBreakpoint(IBreakpointInfo &bp)
  2031. {
  2032. // May want a hash lookup here, but the non-uniqueness makes it slightly non-trivial so bruteforce for now
  2033. HashIterator edges(allProbes);
  2034. ForEach(edges)
  2035. {
  2036. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  2037. if (bp.queryMode()==BreakpointModeGlobal || bp.idMatch(BreakpointModeEdge, edge->queryEdgeId()))
  2038. edge->removeBreakpoint(bp);
  2039. }
  2040. ForEachItemIn(idx, childGraphs)
  2041. {
  2042. childGraphs.item(idx).removeBreakpoint(bp);
  2043. }
  2044. }
  2045. IActivityDebugContext *CBaseDebugGraphManager::lookupActivityByEdgeId(const char *edgeId)
  2046. {
  2047. // MORE - if structured ID's, behave differently?
  2048. IActivityDebugContext *edge = allProbes.getValue(edgeId);
  2049. if (!edge)
  2050. {
  2051. ForEachItemIn(idx, childGraphs)
  2052. {
  2053. edge = childGraphs.item(idx).lookupActivityByEdgeId(edgeId);
  2054. if (edge)
  2055. break;
  2056. }
  2057. }
  2058. return edge;
  2059. }
  2060. const char *CBaseDebugGraphManager::queryGraphName() const
  2061. {
  2062. return graphName.get();
  2063. }
  2064. void CBaseDebugGraphManager::getXGMML(IXmlWriter *output, unsigned sequence, bool isActive)
  2065. {
  2066. // Build xgmml for this graph... as currently executing...
  2067. CriticalBlock b(crit);
  2068. output->outputBeginNested("Graph", true);
  2069. output->outputString(graphName.length(), graphName.get(), "@id"); // MORE here
  2070. output->outputBool(isActive, "@active");
  2071. output->outputBeginNested("xgmml", true);
  2072. outputChildGraph(output, sequence);
  2073. ForEachItemIn(dependencyIdx, dependencies)
  2074. {
  2075. DebugDependencyRecord &dependency = dependencies.item(dependencyIdx);
  2076. if (!sequence || dependency.sequence > sequence) // MORE - if we start reporting any stats on nodes (seeks? scans?) this may need to change
  2077. {
  2078. DebugActivityRecord *source = allActivities.getValue(dependency.sourceActivity);
  2079. DebugActivityRecord *target = allActivities.getValue(dependency.targetActivity);
  2080. if (source && target)
  2081. {
  2082. output->outputBeginNested("edge", true);
  2083. output->outputCString(dependency.edgeId, "@id");
  2084. output->outputBeginNested("att", false); output->outputCString("_dependsOn", "@name"); output->outputInt(1, "@value"); output->outputEndNested("att");
  2085. output->outputBeginNested("att", false); output->outputCString("_sourceActivity", "@name"); source->outputId(output, "@value"); output->outputEndNested("att");
  2086. output->outputBeginNested("att", false); output->outputCString("_targetActivity", "@name"); target->outputId(output, "@value"); output->outputEndNested("att");
  2087. output->outputEndNested("edge");
  2088. }
  2089. // MORE - work out why sometimes source and/or target is NULL
  2090. }
  2091. }
  2092. output->outputEndNested("xgmml");
  2093. output->outputEndNested("Graph");
  2094. }
  2095. void CBaseDebugGraphManager::searchHistories(IXmlWriter *output, IRowMatcher *matcher, bool fullRows)
  2096. {
  2097. HashIterator edges(allProbes); // MORE - could find ones that have named field
  2098. ForEach(edges)
  2099. {
  2100. IActivityDebugContext *edge = allProbes.mapToValue(&edges.query());
  2101. edge->searchHistories(output, matcher, fullRows);
  2102. }
  2103. ForEachItemIn(idx, childGraphs)
  2104. {
  2105. IDebugGraphManager &graph = childGraphs.item(idx);
  2106. graph.searchHistories(output, matcher, fullRows);
  2107. }
  2108. }
  2109. void CBaseDebugGraphManager::serializeProxyGraphs(MemoryBuffer &buff)
  2110. {
  2111. buff.append(childGraphs.length());
  2112. ForEachItemIn(idx, childGraphs)
  2113. {
  2114. IDebugGraphManager &graph = childGraphs.item(idx);
  2115. buff.append(graph.queryId());
  2116. buff.append((__uint64)graph.queryProxyId());
  2117. }
  2118. }
  2119. void CBaseDebugGraphManager::endChildGraph(IProbeManager *child, IActivityBase *parent)
  2120. {
  2121. CriticalBlock b(crit);
  2122. if (parent)
  2123. {
  2124. Linked<DebugActivityRecord> node = allActivities.getValue(parent);
  2125. if (!node)
  2126. {
  2127. node.setown(new DebugActivityRecord(parent, 0, debugContext->queryChannel(), 0));
  2128. allActivities.setValue(parent, node);
  2129. }
  2130. IDebugGraphManager *childManager = QUERYINTERFACE(child, CBaseDebugGraphManager); // yuk
  2131. node->childGraphs.append(*LINK(childManager));
  2132. }
  2133. }