PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/CS/migrated/branches/R0_94/libs/csutil/debug.cpp

#
C++ | 728 lines | 599 code | 63 blank | 66 comment | 138 complexity | 7b2c3b173763ba55b4a4ccdbdc78bd2a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. Debugging tools.
  3. Copyright (C) 2001 by Jorrit Tyberghein
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public
  13. License along with this library; if not, write to the Free
  14. Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <stdarg.h>
  19. #include "cssysdef.h"
  20. #include "csutil/scf.h"
  21. #include "csutil/util.h"
  22. #include "csutil/debug.h"
  23. #include "csutil/scf.h"
  24. #include "iutil/objreg.h"
  25. //-----------------------------------------------------------------------------
  26. struct csDGEL;
  27. // A link element (parent->child or child->parent)
  28. struct csDGELLinkEl
  29. {
  30. csDGEL* link; // Child or parent linked too.
  31. uint32 timestamp; // Time of creation of link.
  32. };
  33. struct csDGEL
  34. {
  35. void* object; // Pointer to the object.
  36. uint32 timestamp; // Timestamp of last allocation.
  37. uint8 scf; // If true 'object' is an iBase.
  38. uint8 used; // If true the object is currently allocated.
  39. uint8 marker; // To see what we already dumped.
  40. uint8 recurse_marker; // To see what we're dumping in this recursion.
  41. uint16 num_parents;
  42. uint16 num_children;
  43. csDGELLinkEl* parents;
  44. csDGELLinkEl* children;
  45. char* description;
  46. char* type;
  47. char* file;
  48. int linenr;
  49. csDGEL ()
  50. {
  51. object = NULL;
  52. scf = false;
  53. used = false;
  54. timestamp = 0;
  55. description = NULL;
  56. type = NULL;
  57. file = NULL;
  58. num_parents = 0;
  59. parents = NULL;
  60. num_children = 0;
  61. children = NULL;
  62. }
  63. void Clear ()
  64. {
  65. delete[] description; description = NULL;
  66. delete[] type; type = NULL;
  67. delete[] parents; parents = NULL; num_parents = 0;
  68. delete[] children; children = NULL; num_children = 0;
  69. file = NULL;
  70. }
  71. ~csDGEL ()
  72. {
  73. Clear ();
  74. }
  75. void AddChild (csDGEL* child, uint32 timestamp)
  76. {
  77. if (!children)
  78. {
  79. children = new csDGELLinkEl[1];
  80. }
  81. else
  82. {
  83. csDGELLinkEl* new_children = new csDGELLinkEl[num_children+1];
  84. memcpy (new_children, children, sizeof (csDGELLinkEl)*num_children);
  85. delete[] children; children = new_children;
  86. }
  87. children[num_children].link = child;
  88. children[num_children++].timestamp = timestamp;
  89. }
  90. void RemoveChild (csDGEL* child)
  91. {
  92. if (!children) return;
  93. if (num_children == 1)
  94. {
  95. if (child == children[0].link)
  96. {
  97. delete[] children; children = NULL;
  98. num_children = 0;
  99. }
  100. return;
  101. }
  102. int i, j = 0;
  103. for (i = 0 ; i < num_children ; i++)
  104. {
  105. if (child != children[i].link) children[j++] = children[i];
  106. }
  107. num_children = j;
  108. }
  109. void AddParent (csDGEL* parent, uint32 timestamp)
  110. {
  111. if (!parents)
  112. {
  113. parents = new csDGELLinkEl[1];
  114. }
  115. else
  116. {
  117. csDGELLinkEl* new_parents = new csDGELLinkEl[num_parents+1];
  118. memcpy (new_parents, parents, sizeof (csDGELLinkEl)*num_parents);
  119. delete[] parents; parents = new_parents;
  120. }
  121. parents[num_parents].link = parent;
  122. parents[num_parents++].timestamp = timestamp;
  123. }
  124. void RemoveParent (csDGEL* parent)
  125. {
  126. if (!parents) return;
  127. if (num_parents == 1)
  128. {
  129. if (parent == parents[0].link)
  130. {
  131. delete[] parents; parents = NULL;
  132. num_parents = 0;
  133. }
  134. return;
  135. }
  136. int i, j = 0;
  137. for (i = 0 ; i < num_parents ; i++)
  138. {
  139. if (parent != parents[i].link) parents[j++] = parents[i];
  140. }
  141. num_parents = j;
  142. }
  143. };
  144. class csDebugGraph : public iBase
  145. {
  146. public:
  147. int num_els;
  148. int max_els;
  149. csDGEL** els;
  150. uint32 last_timestamp;
  151. csDebugGraph ()
  152. {
  153. SCF_CONSTRUCT_IBASE (NULL);
  154. num_els = 0;
  155. max_els = 100;
  156. els = new csDGEL* [max_els];
  157. last_timestamp = 1;
  158. }
  159. virtual ~csDebugGraph ()
  160. {
  161. Clear ();
  162. delete[] els;
  163. }
  164. void Clear ()
  165. {
  166. int i;
  167. for (i = 0 ; i < num_els ; i++)
  168. {
  169. delete els[i];
  170. }
  171. delete[] els;
  172. num_els = 0;
  173. max_els = 100;
  174. els = new csDGEL* [max_els];
  175. last_timestamp = 1;
  176. }
  177. csDGEL* AddEl (void* object)
  178. {
  179. if (num_els >= max_els)
  180. {
  181. max_els += 100;
  182. csDGEL** new_els = new csDGEL* [max_els];
  183. memcpy (new_els, els, sizeof (csDGEL*) * num_els);
  184. delete[] els;
  185. els = new_els;
  186. }
  187. csDGEL* el = new csDGEL ();
  188. els[num_els++] = el;
  189. el->used = false;
  190. el->object = object;
  191. return el;
  192. }
  193. csDGEL* FindEl (void* object)
  194. {
  195. int i;
  196. for (i = 0 ; i < num_els ; i++)
  197. {
  198. if (els[i]->object == object) return els[i];
  199. }
  200. return NULL;
  201. }
  202. SCF_DECLARE_IBASE;
  203. };
  204. SCF_IMPLEMENT_IBASE (csDebugGraph)
  205. SCF_IMPLEMENT_IBASE_END
  206. static csDebugGraph* SetupDebugGraph (iObjectRegistry* object_reg)
  207. {
  208. iBase* idg = CS_QUERY_REGISTRY_TAG (object_reg, "__Debug_Graph__");
  209. if (!idg)
  210. {
  211. idg = new csDebugGraph ();
  212. if (!object_reg->Register (idg, "__Debug_Graph__"))
  213. {
  214. // If registering fails this probably means we are in the destruction
  215. // pass and the object registry doesn't allow new updates anymore.
  216. idg->DecRef ();
  217. return NULL;
  218. }
  219. }
  220. idg->DecRef ();
  221. return (csDebugGraph*)idg;
  222. }
  223. //-----------------------------------------------------------------------------
  224. void csDebuggingGraph::SetupGraph (iObjectRegistry* object_reg)
  225. {
  226. if (!SetupDebugGraph (object_reg)) return;
  227. #ifdef CS_DEBUG
  228. iSCF::SCF->object_reg = object_reg;
  229. #endif
  230. }
  231. void csDebuggingGraph::AddObject (iObjectRegistry* object_reg,
  232. void* object, bool scf, char* file, int linenr,
  233. char* description, ...)
  234. {
  235. #ifdef CS_DEBUG
  236. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  237. #endif
  238. if (!object_reg) return;
  239. csDebugGraph* dg = SetupDebugGraph (object_reg);
  240. if (!dg) return;
  241. csDGEL* el = dg->FindEl (object);
  242. if (el)
  243. {
  244. // The element is already there. This either means that
  245. // the object was freed first and now a new object happens
  246. // to be allocated on the same position (this is a valid
  247. // situation), or else it means that the object is allocated
  248. // twice! This is not a valid situation because it means
  249. // that DG_ADD or DG_ADDI is used with a missing DG_REM
  250. // in between.
  251. if (el->used)
  252. {
  253. printf ("ERROR! Object is added twice to the debug graph!\n");
  254. printf ("%p %s", el->object, el->description);
  255. fflush (stdout);
  256. CS_ASSERT (false);
  257. return;
  258. }
  259. // Reinitialize the element. We will also clear the list of
  260. // parents and children here since this is a new element and the
  261. // previous lists are certainly invalid. Note that it is possible
  262. // that other elements still point to this element from a previous
  263. // incarnation. That case can be detected with the timestamp: timestamp
  264. // of this creation will be bigger than the timestamp of the creation
  265. // of the link to this item. The Dump will show this anomaly.
  266. el->Clear ();
  267. }
  268. else
  269. {
  270. // We have a new element.
  271. el = dg->AddEl (object);
  272. }
  273. el->used = true;
  274. el->timestamp = dg->last_timestamp++;
  275. el->scf = scf;
  276. if (description)
  277. {
  278. char buf[1000];
  279. va_list arg;
  280. va_start (arg, description);
  281. vsprintf (buf, description, arg);
  282. va_end (arg);
  283. el->description = csStrNew (buf);
  284. }
  285. else el->description = NULL;
  286. el->file = file;
  287. el->linenr = linenr;
  288. }
  289. void csDebuggingGraph::AttachDescription (iObjectRegistry* object_reg,
  290. void* object, char* description, ...)
  291. {
  292. #ifdef CS_DEBUG
  293. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  294. #endif
  295. if (!object_reg) return;
  296. csDebugGraph* dg = SetupDebugGraph (object_reg);
  297. if (!dg) return;
  298. csDGEL* el = dg->FindEl (object);
  299. if (el == NULL)
  300. {
  301. printf ("ERROR! Cannot find object %p to add description:\n'", object);
  302. va_list arg;
  303. va_start (arg, description);
  304. vprintf (description, arg);
  305. va_end (arg);
  306. printf ("'\n");
  307. fflush (stdout);
  308. CS_ASSERT (false);
  309. return;
  310. }
  311. delete[] el->description;
  312. if (description)
  313. {
  314. char buf[1000];
  315. va_list arg;
  316. va_start (arg, description);
  317. vsprintf (buf, description, arg);
  318. va_end (arg);
  319. el->description = csStrNew (buf);
  320. }
  321. else el->description = NULL;
  322. }
  323. void csDebuggingGraph::AttachType (iObjectRegistry* object_reg,
  324. void* object, char* type)
  325. {
  326. #ifdef CS_DEBUG
  327. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  328. #endif
  329. if (!object_reg) return;
  330. csDebugGraph* dg = SetupDebugGraph (object_reg);
  331. if (!dg) return;
  332. csDGEL* el = dg->FindEl (object);
  333. if (el == NULL)
  334. {
  335. printf ("ERROR! Cannot find object %p to add type '%s'\n", object, type);
  336. fflush (stdout);
  337. CS_ASSERT (false);
  338. return;
  339. }
  340. delete[] el->type;
  341. if (type)
  342. el->type = csStrNew (type);
  343. else el->type = NULL;
  344. }
  345. void csDebuggingGraph::RemoveObject (iObjectRegistry* object_reg,
  346. void* object, char* file, int linenr)
  347. {
  348. #ifdef CS_DEBUG
  349. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  350. #endif
  351. if (!object_reg) return;
  352. (void)file;
  353. (void)linenr;
  354. csDebugGraph* dg = SetupDebugGraph (object_reg);
  355. if (!dg) return;
  356. csDGEL* el = dg->FindEl (object);
  357. if (!el)
  358. {
  359. printf ("ERROR! Cannot find element for object %p!\n", object);
  360. fflush (stdout);
  361. CS_ASSERT (false);
  362. return;
  363. }
  364. if (!el->used)
  365. {
  366. printf ("ERROR! Element for object %p is not allocated!\n", object);
  367. fflush (stdout);
  368. CS_ASSERT (false);
  369. return;
  370. }
  371. el->used = false;
  372. }
  373. void csDebuggingGraph::AddParent (iObjectRegistry* object_reg,
  374. void* child, void* parent)
  375. {
  376. #ifdef CS_DEBUG
  377. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  378. #endif
  379. if (!object_reg) return;
  380. csDebugGraph* dg = SetupDebugGraph (object_reg);
  381. if (!dg) return;
  382. csDGEL* p_el = dg->FindEl (parent);
  383. // If parent could not be found. Create a dummy place holder for later.
  384. if (!p_el) p_el = dg->AddEl (parent);
  385. csDGEL* c_el = dg->FindEl (child);
  386. // If child could not be found. Create a dummy place holder for later.
  387. if (!c_el) c_el = dg->AddEl (child);
  388. c_el->AddParent (p_el, dg->last_timestamp++);
  389. }
  390. void csDebuggingGraph::AddChild (iObjectRegistry* object_reg,
  391. void* parent, void* child)
  392. {
  393. #ifdef CS_DEBUG
  394. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  395. #endif
  396. if (!object_reg) return;
  397. csDebugGraph* dg = SetupDebugGraph (object_reg);
  398. if (!dg) return;
  399. csDGEL* p_el = dg->FindEl (parent);
  400. // If parent could not be found. Create a dummy place holder for later.
  401. if (!p_el) p_el = dg->AddEl (parent);
  402. csDGEL* c_el = dg->FindEl (child);
  403. // If child could not be found. Create a dummy place holder for later.
  404. if (!c_el) c_el = dg->AddEl (child);
  405. p_el->AddChild (c_el, dg->last_timestamp++);
  406. }
  407. void csDebuggingGraph::RemoveParent (iObjectRegistry* object_reg,
  408. void* child, void* parent)
  409. {
  410. #ifdef CS_DEBUG
  411. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  412. #endif
  413. if (!object_reg) return;
  414. csDebugGraph* dg = SetupDebugGraph (object_reg);
  415. if (!dg) return;
  416. csDGEL* c_el = dg->FindEl (child);
  417. if (!c_el) return; // Nothing to do if child is not there.
  418. csDGEL* p_el = dg->FindEl (parent);
  419. if (!p_el) return; // Nothing to do if parent doesn't exist either.
  420. c_el->RemoveParent (p_el);
  421. }
  422. void csDebuggingGraph::RemoveChild (iObjectRegistry* object_reg,
  423. void* parent, void* child)
  424. {
  425. #ifdef CS_DEBUG
  426. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  427. #endif
  428. if (!object_reg) return;
  429. csDebugGraph* dg = SetupDebugGraph (object_reg);
  430. if (!dg) return;
  431. csDGEL* p_el = dg->FindEl (parent);
  432. if (!p_el) return; // Nothing to do.
  433. csDGEL* c_el = dg->FindEl (child);
  434. if (!c_el) return; // Nothing to do.
  435. p_el->RemoveChild (c_el);
  436. }
  437. void csDebuggingGraph::Clear (iObjectRegistry* object_reg)
  438. {
  439. #ifdef CS_DEBUG
  440. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  441. #endif
  442. if (!object_reg) return;
  443. csDebugGraph* dg = SetupDebugGraph (object_reg);
  444. if (!dg) return;
  445. dg->Clear ();
  446. }
  447. void csDebuggingGraph::Dump (iObjectRegistry* object_reg)
  448. {
  449. #ifdef CS_DEBUG
  450. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  451. #endif
  452. if (!object_reg) return;
  453. csDebugGraph* dg = SetupDebugGraph (object_reg);
  454. if (!dg) return;
  455. csDGEL** els = dg->els;
  456. // First mark all elements as unused and count the number
  457. // of elements we have.
  458. int i, cnt = 0;
  459. for (i = 0 ; i < dg->num_els ; i++)
  460. {
  461. if (els[i]->used)
  462. {
  463. cnt++;
  464. els[i]->marker = false;
  465. }
  466. else
  467. els[i]->marker = true;
  468. els[i]->recurse_marker = false;
  469. }
  470. printf ("====================================================\n");
  471. printf ("Total number of used objects in graph: %d\n", cnt);
  472. // Find the first unmarked object and dump it.
  473. i = 0;
  474. while (i < dg->num_els)
  475. {
  476. if (!els[i]->marker)
  477. {
  478. Dump (object_reg, els[i]->object, false);
  479. i = 0; // Restart scan.
  480. printf ("----------------------------------------------------\n");
  481. }
  482. else i++;
  483. }
  484. fflush (stdout);
  485. }
  486. static void DumpSubTree (int indent, const char* type, uint32 link_timestamp,
  487. csDGEL* el)
  488. {
  489. // link_timestamp is the timestamp when the link was created.
  490. char spaces[1000];
  491. int ind = indent;
  492. if (ind > 999) ind = 999;
  493. char* sp = spaces;
  494. while (ind >= 10)
  495. {
  496. strcpy (sp, " ");
  497. sp += 10;
  498. ind -= 10;
  499. }
  500. while (ind >= 1)
  501. {
  502. *sp++ = ' ';
  503. ind--;
  504. }
  505. *sp = 0;
  506. if (el->recurse_marker && *type == 'P')
  507. {
  508. // We already encountered this object in this recursion. So we just
  509. // put a short-hand here.
  510. printf ("%s%s(%lu) %p <-\n", spaces, type, link_timestamp, el->object);
  511. return;
  512. }
  513. // Show the ref count if it is an scf interface. If the object
  514. // is no longer used then show '?' instead of ref count to avoid
  515. // calling an invalid pointer.
  516. printf ("%s%s(%lu) %p(", spaces, type, link_timestamp, el->object);
  517. if (el->scf)
  518. {
  519. if (el->used)
  520. printf ("r%d,", ((iBase*)(el->object))->GetRefCount ());
  521. else
  522. printf ("r?,-");
  523. }
  524. else if (!el->used)
  525. printf ("-");
  526. if (el->type)
  527. printf ("t%lu) %s(%s)", el->timestamp, el->type, el->description);
  528. else
  529. printf ("t%lu) %s", el->timestamp, el->description);
  530. // If the object is used but the link to this object was created
  531. // BEFORE the object (i.e. timestamps) then this is at least very
  532. // suspicious and is also marked as such.
  533. if (el->used && link_timestamp > 0 && link_timestamp < el->timestamp)
  534. {
  535. printf (" (SUSPICIOUS!)");
  536. }
  537. if (el->marker || *type == 'P' || !el->used)
  538. {
  539. if (el->used)
  540. {
  541. if (el->marker)
  542. printf (" (REF)\n");
  543. else
  544. printf ("\n");
  545. }
  546. else
  547. printf (" (BAD LINK!)\n");
  548. if (*type != 'P') el->marker = true;
  549. }
  550. else
  551. {
  552. el->marker = true;
  553. printf (" (%s,%d) #p=%d #c=%d\n",
  554. el->file, el->linenr, el->num_parents, el->num_children);
  555. if (el->num_parents + el->num_children > 0)
  556. {
  557. bool use_brackets = true;
  558. if (el->num_children == 0 && el->num_parents == 1)
  559. {
  560. // If we have only one parent and no children we check
  561. // if we already visited that parent in this recursion.
  562. // In that case we don't print the brackets.
  563. if (el->parents[0].link->recurse_marker) use_brackets = false;
  564. }
  565. if (use_brackets) printf ("%s{\n", spaces);
  566. el->recurse_marker = true;
  567. int i;
  568. for (i = 0 ; i < el->num_parents ; i++)
  569. {
  570. DumpSubTree (indent+2, "P", el->parents[i].timestamp,
  571. el->parents[i].link);
  572. }
  573. for (i = 0 ; i < el->num_children ; i++)
  574. {
  575. DumpSubTree (indent+2, "C", el->children[i].timestamp,
  576. el->children[i].link);
  577. }
  578. el->recurse_marker = false;
  579. if (use_brackets) printf ("%s}\n", spaces);
  580. }
  581. }
  582. fflush (stdout);
  583. }
  584. static int compare_el (const void* vel1, const void* vel2)
  585. {
  586. csDGEL* el1 = *(csDGEL**)vel1;
  587. csDGEL* el2 = *(csDGEL**)vel2;
  588. if (el1->num_parents < el2->num_parents) return -1;
  589. else if (el1->num_parents > el2->num_parents) return 1;
  590. else return 0;
  591. }
  592. void csDebuggingGraph::Dump (iObjectRegistry* object_reg, void* object,
  593. bool reset_mark)
  594. {
  595. #ifdef CS_DEBUG
  596. if (!object_reg) object_reg = iSCF::SCF->object_reg;
  597. #endif
  598. if (!object_reg) return;
  599. csDebugGraph* dg = SetupDebugGraph (object_reg);
  600. if (!dg) return;
  601. int i;
  602. if (reset_mark)
  603. {
  604. // First mark all elements as unused.
  605. csDGEL** els = dg->els;
  606. for (i = 0 ; i < dg->num_els ; i++)
  607. {
  608. if (els[i]->used) els[i]->marker = false;
  609. else els[i]->marker = true;
  610. els[i]->recurse_marker = false;
  611. }
  612. }
  613. csDGEL* el = dg->FindEl (object);
  614. CS_ASSERT (el != NULL);
  615. // First copy all elements that belong to this sub-graph
  616. // to a local array.
  617. csDGEL** local_els = new csDGEL* [dg->num_els];
  618. int done = 0, num = 0;
  619. local_els[num++] = el; el->marker = true;
  620. while (done < num)
  621. {
  622. csDGEL* lel = local_els[done++];
  623. if (lel->used)
  624. {
  625. for (i = 0 ; i < lel->num_parents ; i++)
  626. {
  627. if (!lel->parents[i].link->marker)
  628. {
  629. local_els[num++] = lel->parents[i].link;
  630. lel->parents[i].link->marker = true;
  631. }
  632. }
  633. for (i = 0 ; i < lel->num_children ; i++)
  634. if (!lel->children[i].link->marker)
  635. {
  636. local_els[num++] = lel->children[i].link;
  637. lel->children[i].link->marker = true;
  638. }
  639. }
  640. }
  641. // Now mark all elements as unused again.
  642. for (i = 0 ; i < num ; i++)
  643. local_els[i]->marker = false;
  644. // Sort all elements based on the number of parents.
  645. // This means that 'root' like elements will come first in the
  646. // array.
  647. qsort (local_els, num, sizeof (csDGEL*), compare_el);
  648. // Now dump all parents here until all are marked.
  649. for (i = 0 ; i < num ; i++)
  650. {
  651. if (!local_els[i]->used)
  652. {
  653. local_els[i]->marker = true;
  654. }
  655. else if (!local_els[i]->marker)
  656. {
  657. DumpSubTree (0, "R", 0, local_els[i]);
  658. }
  659. }
  660. delete[] local_els;
  661. }
  662. //-----------------------------------------------------------------------------