summaryrefslogtreecommitdiffstats
path: root/kcachegrind/kcachegrind/stackbrowser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kcachegrind/kcachegrind/stackbrowser.cpp')
-rw-r--r--kcachegrind/kcachegrind/stackbrowser.cpp417
1 files changed, 417 insertions, 0 deletions
diff --git a/kcachegrind/kcachegrind/stackbrowser.cpp b/kcachegrind/kcachegrind/stackbrowser.cpp
new file mode 100644
index 00000000..783edf99
--- /dev/null
+++ b/kcachegrind/kcachegrind/stackbrowser.cpp
@@ -0,0 +1,417 @@
+/* This file is part of KCachegrind.
+ Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
+
+ KCachegrind is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation, version 2.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qlistview.h>
+
+#include "stackbrowser.h"
+
+// Stack
+
+Stack::Stack(TraceFunction* top, TraceCallList calls)
+{
+ _refCount = 0;
+ _top = top;
+ _calls = calls;
+
+ extendBottom();
+}
+
+Stack::Stack(TraceFunction* f)
+{
+ _refCount = 0;
+ _top = f;
+
+ extendBottom();
+ extendTop();
+}
+
+void Stack::extendBottom()
+{
+ TraceCallList l;
+ TraceCall *c, *call;
+ SubCost most;
+ TraceFunction* f;
+
+ if (_calls.last())
+ f = _calls.last()->called();
+ else
+ f = _top;
+
+ if (!f) return;
+ // don't follow calls from cycles
+ if (f->cycle() == f) return;
+
+
+ int max = 30;
+
+ // try to extend to lower stack frames
+ while (f && (max-- >0)) {
+ l = f->callings();
+ call = 0;
+ most = 0;
+ for (c=l.first();c;c=l.next()) {
+ // no cycle calls in stack: could be deleted without notice
+ if (c->called()->cycle() == c->called()) continue;
+ // no simple recursions
+ if (c->called() == _top) continue;
+
+ if (c->called()->name().isEmpty()) continue;
+ SubCost sc = c->subCost(0); // FIXME
+ if (sc == 0) continue;
+
+ if (sc > most) {
+ most = sc;
+ call = c;
+ }
+ }
+ if (!call)
+ break;
+
+ _calls.append(call);
+ f = call->called();
+ }
+}
+
+
+void Stack::extendTop()
+{
+ TraceCallList l;
+ TraceCall *c, *call;
+ SubCost most;
+
+ int max = 10;
+
+ // don't follow calls from cycles
+ if (_top->cycle() == _top) return;
+
+ // try to extend to upper stack frames
+ while (_top && (max-- >0)) {
+ l = _top->callers();
+ call = 0;
+ most = 0;
+ for (c=l.first();c;c=l.next()) {
+ // no cycle calls in stack: could be deleted without notice
+ if (c->caller()->cycle() == c->caller()) continue;
+ // no simple recursions
+ if (c->caller() == _top) continue;
+
+ if (c->caller()->name().isEmpty()) continue;
+ SubCost sc = c->subCost(0); // FIXME
+ if (sc == 0) continue;
+
+ if (sc > most) {
+ most = sc;
+ call = c;
+ }
+ }
+ if (!call)
+ break;
+
+ _calls.prepend(call);
+ _top = call->caller();
+ }
+}
+
+TraceFunction* Stack::caller(TraceFunction* fn, bool extend)
+{
+ TraceFunction* f;
+ TraceCall* c;
+
+ if (extend && (_top == fn)) {
+ // extend at top
+ extendTop();
+ f = _top;
+ }
+
+ for (c=_calls.first();c;c=_calls.next()) {
+ f = c->called();
+ if (f == fn)
+ return c->caller();
+ }
+ return 0;
+}
+
+TraceFunction* Stack::called(TraceFunction* fn, bool extend)
+{
+ TraceFunction* f;
+ TraceCall* c;
+
+ for (c=_calls.first();c;c=_calls.next()) {
+ f = c->caller();
+ if (f == fn)
+ return c->called();
+ }
+
+ if (extend && (c->called() == fn)) {
+ // extend at bottom
+ extendBottom();
+
+ // and search again
+ for (c=_calls.first();c;c=_calls.next()) {
+ f = c->caller();
+ if (f == fn)
+ return c->called();
+ }
+ }
+
+ return 0;
+}
+
+bool Stack::contains(TraceFunction* fn)
+{
+ // cycles are listed on there own
+ if (fn->cycle() == fn) return false;
+ if (_top->cycle() == _top) return false;
+
+ if (fn == _top)
+ return true;
+
+ TraceFunction* f = _top;
+ TraceCall* c;
+
+ for (c=_calls.first();c;c=_calls.next()) {
+ f = c->called();
+ if (f == fn)
+ return true;
+ }
+
+ TraceCallList l;
+
+ // try to extend at bottom (even if callCount 0)
+ l = f->callings();
+ for (c=l.first();c;c=l.next()) {
+ f = c->called();
+ if (f == fn)
+ break;
+ }
+
+ if (c) {
+ _calls.append(c);
+
+ // extend at bottom after found one
+ extendBottom();
+ return true;
+ }
+
+ // try to extend at top (even if callCount 0)
+ l = _top->callers();
+ for (c=l.first();c;c=l.next()) {
+ f = c->caller();
+ if (f == fn)
+ break;
+ }
+
+ if (c) {
+ _calls.prepend(c);
+
+ // extend at top after found one
+ extendTop();
+ return true;
+ }
+
+ return false;
+}
+
+Stack* Stack::split(TraceFunction* f)
+{
+ TraceCallList calls = _calls;
+ TraceCall *c, *c2;
+
+ // cycles are listed on there own
+ if (f->cycle() == f) return 0;
+ if (_top->cycle() == _top) return false;
+
+ for (c=calls.first();c;c=calls.next()) {
+ TraceCallList l = c->called()->callings();
+ for (c2=l.first();c2;c2=l.next()) {
+ if (c2 == c) continue;
+ if (c2->called() == f)
+ break;
+ }
+ if (c2)
+ break;
+ }
+
+ if (!c)
+ return 0;
+
+ // remove bottom part
+ calls.last();
+ while (calls.current() && calls.current()!=c)
+ calls.removeLast();
+
+ calls.append(c2);
+ return new Stack(_top, calls );
+}
+
+QString Stack::toString()
+{
+ QString res = _top->name();
+ TraceCall *c;
+ for (c=_calls.first();c;c=_calls.next())
+ res += "\n > " + c->called()->name();
+
+ return res;
+}
+
+
+// HistoryItem
+
+HistoryItem::HistoryItem(Stack* stack, TraceFunction* function)
+{
+ _stack = stack;
+ _function = function;
+ if (_stack)
+ _stack->ref();
+
+ _last = 0;
+ _next = 0;
+
+/*
+ qDebug("New Stack History Item (sRef %d): %s\n %s",
+ _stack->refCount(), _function->name().ascii(),
+ _stack->toString().ascii());
+*/
+}
+
+HistoryItem::~HistoryItem()
+{
+ if (0) qDebug("Deleting Stack History Item (sRef %d): %s",
+ _stack->refCount(),
+ _function->name().ascii());
+
+ if (_last)
+ _last->_next = _next;
+ if (_stack) {
+ if (_stack->deref() == 0)
+ delete _stack;
+ }
+}
+
+
+// StackBrowser
+
+StackBrowser::StackBrowser()
+{
+ _current = 0;
+}
+
+StackBrowser::~StackBrowser()
+{
+ delete _current;
+}
+
+HistoryItem* StackBrowser::select(TraceFunction* f)
+{
+ if (!_current) {
+ Stack* s = new Stack(f);
+ _current = new HistoryItem(s, f);
+ }
+ else if (_current->function() != f) {
+ // make current item the last one
+ HistoryItem* item = _current;
+ if (item->next()) {
+ item = item->next();
+ item->last()->setNext(0);
+
+ while (item->next()) {
+ item = item->next();
+ delete item->last();
+ }
+ delete item;
+ }
+
+ Stack* s = _current->stack();
+ if (!s->contains(f)) {
+ s = s->split(f);
+ if (!s)
+ s = new Stack(f);
+ }
+
+ item = _current;
+ _current = new HistoryItem(s, f);
+ item->setNext(_current);
+ _current->setLast(item);
+ }
+
+ // qDebug("Selected %s in StackBrowser", f->name().ascii());
+
+ return _current;
+}
+
+HistoryItem* StackBrowser::goBack()
+{
+ if (_current && _current->last())
+ _current = _current->last();
+
+ return _current;
+}
+
+HistoryItem* StackBrowser::goForward()
+{
+ if (_current && _current->next())
+ _current = _current->next();
+
+ return _current;
+}
+
+HistoryItem* StackBrowser::goUp()
+{
+ if (_current) {
+ TraceFunction* f = _current->stack()->caller(_current->function(), true);
+ if (f)
+ _current = select(f);
+ }
+
+ return _current;
+}
+
+HistoryItem* StackBrowser::goDown()
+{
+ if (_current) {
+ TraceFunction* f = _current->stack()->called(_current->function(), true);
+ if (f)
+ _current = select(f);
+ }
+
+ return _current;
+}
+
+bool StackBrowser::canGoBack()
+{
+ return _current && _current->last();
+}
+
+bool StackBrowser::canGoForward()
+{
+ return _current && _current->next();
+}
+
+bool StackBrowser::canGoUp()
+{
+ if (!_current) return false;
+
+ return _current->stack()->caller(_current->function(), false);
+}
+
+bool StackBrowser::canGoDown()
+ {
+ if (!_current) return false;
+
+ return _current->stack()->called(_current->function(), false);
+}