From e3a546d81cd115d7cd9105fc31ecccecfb48b71d Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 22 Dec 2009 11:35:38 +0100 Subject: grapher: scroll continuously with time Don't scale graph based on how much data will fit. This didn't work very well and resulted in distracting, weird scale changes. We now assume that scripts output their time (x axis) in milliseconds. * grapher/Graph.hxx (setCurrentTime): New function. * grapher/Graph.cxx (Graph::draw): Assume a fixed default scale of 1 pixel = 5 milliseconds and don't do any autoscaling. * grapher/GraphWidget.cxx (GraphWidget constructor): Set global time base on startup. (on_expose_event): Don't search graphs for earliest time. * grapher/GraphWidget.hxx (_timeBaseInitialized): delete * grapher/Time.hxx: new file; interface to timeval. --- grapher/Graph.cxx | 55 ++++---------------------------- grapher/Graph.hxx | 2 ++ grapher/GraphWidget.cxx | 25 +++------------ grapher/GraphWidget.hxx | 1 - grapher/Time.hxx | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 70 deletions(-) create mode 100644 grapher/Time.hxx diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index dc808fa4..27b6a24f 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.cxx @@ -19,7 +19,9 @@ using namespace std::tr1; GraphDataList GraphDataBase::graphData; sigc::signal GraphDataBase::graphDataChanged; - + +int64_t Graph::_currentTime = 0; + Graph::Graph(double x, double y) : _width(600), _height(200), _graphX(0), _graphY(0), _graphWidth(580), _graphHeight(180), @@ -39,55 +41,10 @@ void Graph::draw(Cairo::RefPtr cr) if (_autoScaling) { - // line separation - int linesPossible = (int)(_graphWidth / (_lineWidth + 2.0)); // Find latest time. - int64_t latestTime = 0; - for (GraphDataList::iterator ditr = _datasets.begin(), - de = _datasets.end(); - ditr != de; - ++ditr) - { - if (!(*ditr)->times.empty()) - { - int64_t lastDataTime = (*ditr)->times.back(); - if (lastDataTime > latestTime) - latestTime = lastDataTime; - } - } - int64_t minDiff = 0; - int64_t maxTotal = 0; - for (GraphDataList::iterator ditr = _datasets.begin(), - de = _datasets.end(); - ditr != de; - ++ditr) - { - GraphDataBase::TimeList& gtimes = (*ditr)->times; - if (gtimes.size() <= 1) - continue; - double totalDiff = 0.0; - for (GraphDataBase::TimeList::reverse_iterator ritr = gtimes.rbegin(), - re = gtimes.rend(); - ritr + 1 != gtimes.rend(); - ritr++) - { - int64_t timeDiff = *ritr - *(ritr + 1); - if (timeDiff < minDiff || (timeDiff != 0 && minDiff == 0)) - minDiff = timeDiff; - if (minDiff != 0 - && ((totalDiff + timeDiff) / minDiff + 1) > linesPossible) - break; - totalDiff += timeDiff; - } - if (totalDiff > maxTotal) - maxTotal = totalDiff; - } - // Now we have a global scale. - _right = latestTime; - if (maxTotal != 0) - _left = latestTime - maxTotal; - else - _left = _right - 1; + _right = _currentTime / 1000; + // Assume 1 pixel = 5 milliseconds + _left = _right - 5000; } cr->save(); double horizScale diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index efabd22d..d9f615b3 100644 --- a/grapher/Graph.hxx +++ b/grapher/Graph.hxx @@ -50,12 +50,14 @@ public: GraphDataList& getDatasets() { return _datasets; } int64_t getTimeAtPoint(double x); void window2GraphCoords(double x, double y, double& xgraph, double& ygraph); + static void setCurrentTime(int64_t time) { _currentTime = time; } protected: GraphDataList _datasets; int64_t _left; int64_t _right; double _top; double _bottom; + static int64_t _currentTime; }; } #endif diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index db7493fb..be1ebdcd 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -23,6 +23,7 @@ #include "GraphWidget.hxx" #include "CairoWidget.hxx" +#include "Time.hxx" namespace systemtap { @@ -33,7 +34,7 @@ using namespace std::tr1; GraphWidget::GraphWidget() : _trackingDrag(false), _width(600), _height(200), _mouseX(0.0), - _mouseY(0.0), _globalTimeBase(0), _timeBaseInitialized(false) + _mouseY(0.0), _globalTimeBase(Time::getAbs() / 1000) { add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK); @@ -157,27 +158,11 @@ bool GraphWidget::on_expose_event(GdkEventExpose* event) cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); - if (!_timeBaseInitialized && !getGraphData().empty()) - { - GraphDataList& graphData = getGraphData(); - int64_t earliest = INT64_MAX; - for (GraphDataList::iterator gd = graphData.begin(), - end = graphData.end(); - gd != end; - ++gd) - { - if (!(*gd)->times.empty() && (*gd)->times[0] < earliest) - earliest = (*gd)->times[0]; - } - if (earliest != INT64_MAX) - { - _globalTimeBase = earliest; - _timeBaseInitialized = true; - } - } + int64_t currentTime = Time::getAbs(); + Graph::setCurrentTime(currentTime); for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) { - if (_displayRelativeTimes && _timeBaseInitialized) + if (_displayRelativeTimes) (*g)->_timeBase = _globalTimeBase; else (*g)->_timeBase = 0.0; diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index f90da361..f2801cd2 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -81,7 +81,6 @@ protected: double _mouseX; double _mouseY; int64_t _globalTimeBase; - bool _timeBaseInitialized; std::tr1::shared_ptr getGraphUnderPoint(double x, double y); void establishHoverTimeout(); Gtk::CheckButton* _relativeTimesButton; diff --git a/grapher/Time.hxx b/grapher/Time.hxx new file mode 100644 index 00000000..7e16b7b2 --- /dev/null +++ b/grapher/Time.hxx @@ -0,0 +1,83 @@ +// systemtap grapher +// Copyright (C) 2009 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + +#ifndef SYSTEMTAP_GRAPHER_TIME_HXX +#define SYSTEMTAP_GRAPHER_TIME_HXX 1 + +#include +#include + +namespace systemtap +{ + +template +class Singleton +{ +public: + static T& instance() + { + static T _instance; + return _instance; + } +protected: + Singleton() {} +private: + // Insure that singleton is constructed before main() is called. + struct InstanceBuilder + { + InstanceBuilder() + { + instance(); + } + }; + static InstanceBuilder _instanceBuilder; +}; + +template +typename Singleton::InstanceBuilder Singleton::_instanceBuilder; + +class Time : public Singleton