roxiedebug.cpp 78 KB

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