diff --git a/rbkit-charts/examples/demo.js b/rbkit-charts/examples/demo.js index 0dd4bde..c3a022d 100644 --- a/rbkit-charts/examples/demo.js +++ b/rbkit-charts/examples/demo.js @@ -19,8 +19,9 @@ var heapRandomData = function () { }; }; +var heapTimeout; var heapCharts = function() { - setTimeout(heapCharts, 1000); + heapTimeout = setTimeout(heapCharts, 1000); Rbkit.updateLiveObjectsChart(heapRandomData()); Rbkit.updateHeapChart(heapRandomData()); @@ -32,14 +33,16 @@ heapCharts(); // =============================== code for gc charts ============================== // assuming max gc time is 10s +var gcStartTimeout; var gcChartStart = function () { - setTimeout(gcChartEnd, _.random(0, 10) * 1000); + gcStartTimeout = setTimeout(gcChartEnd, _.random(0, 10) * 1000); Rbkit.gcStarted(new Date()); }; +var gcEndTimeout; var gcChartEnd = function () { - setTimeout(gcChartStart, 5000); + gcEndTimeout = setTimeout(gcChartStart, 5000); Rbkit.gcEnded(new Date()); }; @@ -108,10 +111,28 @@ var gcRandomStats = function () { }; }; +var gcStatsTimeout; var gcStatsTable = function () { - setTimeout(gcStatsTable, 5000); + gcStatsTimeout = setTimeout(gcStatsTable, 5000); Rbkit.updateGcStats(gcRandomStats()); }; gcStatsTable(); + +var reset = function() { + clearTimeout(heapTimeout); + clearTimeout(gcEndTimeout); + clearTimeout(gcStartTimeout); + clearTimeout(gcStatsTimeout); + + Rbkit.reset(); + + setTimeout(function() { + heapCharts(); + gcChartStart(); + gcStatsTable(); + }, 2000); +}; + +document.getElementById("reset").addEventListener("click", reset); diff --git a/rbkit-charts/examples/index.html b/rbkit-charts/examples/index.html index 1bcfac3..3d123d4 100644 --- a/rbkit-charts/examples/index.html +++ b/rbkit-charts/examples/index.html @@ -43,6 +43,9 @@

GC Stats

+
+ +
diff --git a/rbkit-charts/src/rbcharts.js b/rbkit-charts/src/rbcharts.js index ee610b8..7b0f39e 100644 --- a/rbkit-charts/src/rbcharts.js +++ b/rbkit-charts/src/rbcharts.js @@ -90,7 +90,6 @@ var Rbkit = { // function to record gc end time gcEnded: function (timestamp) { if (!this.gcStartTime) { - console.info("not sure why gc start time is not set"); return; } @@ -155,6 +154,11 @@ var Rbkit = { }, updateGcStats: function (gcStats) { + if (Object.keys(gcStats).length === 0) { + document.getElementById('gc-stats-table').lastElementChild.innerHTML = ''; + return; + } + var gcStatsKeys = Object.keys(this.gcStatsImportantFields); for (var iter = 0; iter != gcStatsKeys.length; ++iter) { var token = gcStatsKeys[iter]; @@ -231,6 +235,7 @@ var Rbkit = { insertLegend: function (chart, canvasDiv) { var chartLegend = chart.generateLegend(); var node = this.stringToNode(chartLegend); + canvasDiv.parentNode.lastChild.remove(); canvasDiv.parentNode.appendChild(node); }, @@ -300,7 +305,14 @@ var Rbkit = { this.updateLiveObjectsChart(data.payload); this.updateHeapChart(data.payload); break; + case "disconnected": + this.reset(); + break; } + }, + + reset: function() { + location.reload(); } }; diff --git a/rbkit-charts/vendor/chart.js b/rbkit-charts/vendor/chart.js index ac77f74..e34e08a 100644 --- a/rbkit-charts/vendor/chart.js +++ b/rbkit-charts/vendor/chart.js @@ -1,10 +1,9 @@ - /*! * Chart.js * http://chartjs.org/ - * Version: 1.0.1-beta.4 + * Version: 1.0.1 * - * Copyright 2014 Nick Downie + * Copyright 2015 Nick Downie * Released under the MIT license * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md */ @@ -93,12 +92,15 @@ // Boolean - whether or not the chart should be responsive and resize when the browser does. responsive: false, - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: true, + // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container + maintainAspectRatio: true, // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove showTooltips: true, + // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function + customTooltips: false, + // Array - Array of string names to attach tooltip events tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], @@ -239,7 +241,7 @@ if (filterCallback(currentItem)){ return currentItem; } - }; + } }, findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){ // Default to end of the array @@ -251,7 +253,7 @@ if (filterCallback(currentItem)){ return currentItem; } - }; + } }, inherits = helpers.inherits = function(extensions){ //Basic javascript inheritance based on the model created in Backbone.js @@ -442,7 +444,9 @@ //Templating methods //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ template = helpers.template = function(templateString, valuesObject){ - // If templateString is function rather than string-template - call the function for valuesObject + + // If templateString is function rather than string-template - call the function for valuesObject + if(templateString instanceof Function){ return templateString(valuesObject); } @@ -831,7 +835,7 @@ newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas); canvas.width = this.chart.width = newWidth; - canvas.height = this.chart.height = newHeight; + canvas.height = this.chart.height = newHeight; retinaScale(this.chart); @@ -867,6 +871,21 @@ destroy : function(){ this.clear(); unbindEvents(this, this.events); + var canvas = this.chart.canvas; + + // Reset canvas height/width attributes starts a fresh with the canvas context + canvas.width = this.chart.width; + canvas.height = this.chart.height; + + // < IE9 doesn't support removeProperty + if (canvas.style.removeProperty) { + canvas.style.removeProperty('width'); + canvas.style.removeProperty('height'); + } else { + canvas.style.removeAttribute('width'); + canvas.style.removeAttribute('height'); + } + delete Chart.instances[this.id]; }, showTooltip : function(ChartElements, forceRedraw){ @@ -896,6 +915,9 @@ this.activeElements = ChartElements; } this.draw(); + if(this.options.customTooltips){ + this.options.customTooltips(false); + } if (ChartElements.length > 0){ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index if (this.datasets && this.datasets.length > 1) { @@ -976,7 +998,8 @@ legendColorBackground : this.options.multiTooltipKeyBackground, title: ChartElements[0].label, chart: this.chart, - ctx: this.chart.ctx + ctx: this.chart.ctx, + custom: this.options.customTooltips }).draw(); } else { @@ -995,7 +1018,8 @@ caretHeight: this.options.tooltipCaretSize, cornerRadius: this.options.tooltipCornerRadius, text: template(this.options.tooltipTemplate, Element), - chart: this.chart + chart: this.chart, + custom: this.options.customTooltips }).draw(); }, this); } @@ -1251,7 +1275,7 @@ this.yAlign = "above"; //Distance between the actual element.y position and the start of the tooltip caret - var caretPadding = 2; + var caretPadding = this.caretPadding = 2; var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, tooltipRectHeight = this.fontSize + 2*this.yPadding, @@ -1273,47 +1297,53 @@ ctx.fillStyle = this.fillColor; - switch(this.yAlign) - { - case "above": - //Draw a caret above the x/y - ctx.beginPath(); - ctx.moveTo(this.x,this.y - caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.closePath(); - ctx.fill(); - break; - case "below": - tooltipY = this.y + caretPadding + this.caretHeight; - //Draw a caret below the x/y - ctx.beginPath(); - ctx.moveTo(this.x, this.y + caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.closePath(); - ctx.fill(); - break; + // Custom Tooltips + if(this.custom){ + this.custom(this); } + else{ + switch(this.yAlign) + { + case "above": + //Draw a caret above the x/y + ctx.beginPath(); + ctx.moveTo(this.x,this.y - caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.closePath(); + ctx.fill(); + break; + case "below": + tooltipY = this.y + caretPadding + this.caretHeight; + //Draw a caret below the x/y + ctx.beginPath(); + ctx.moveTo(this.x, this.y + caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.closePath(); + ctx.fill(); + break; + } - switch(this.xAlign) - { - case "left": - tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); - break; - case "right": - tooltipX = this.x - (this.cornerRadius + this.caretHeight); - break; - } + switch(this.xAlign) + { + case "left": + tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); + break; + case "right": + tooltipX = this.x - (this.cornerRadius + this.caretHeight); + break; + } - drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); + drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); - ctx.fill(); + ctx.fill(); - ctx.fillStyle = this.textColor; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); + ctx.fillStyle = this.textColor; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); + } } }); @@ -1367,36 +1397,42 @@ }, draw : function(){ - drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); - var ctx = this.ctx; - ctx.fillStyle = this.fillColor; - ctx.fill(); - ctx.closePath(); + // Custom Tooltips + if(this.custom){ + this.custom(this); + } + else{ + drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); + var ctx = this.ctx; + ctx.fillStyle = this.fillColor; + ctx.fill(); + ctx.closePath(); - ctx.textAlign = "left"; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.titleTextColor; - ctx.font = this.titleFont; + ctx.textAlign = "left"; + ctx.textBaseline = "middle"; + ctx.fillStyle = this.titleTextColor; + ctx.font = this.titleFont; - ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); + ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); - ctx.font = this.font; - helpers.each(this.labels,function(label,index){ - ctx.fillStyle = this.textColor; - ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); + ctx.font = this.font; + helpers.each(this.labels,function(label,index){ + ctx.fillStyle = this.textColor; + ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); - //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) - //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - //Instead we'll make a white filled block to put the legendColour palette over. + //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) + //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + //Instead we'll make a white filled block to put the legendColour palette over. - ctx.fillStyle = this.legendColorBackground; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + ctx.fillStyle = this.legendColorBackground; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - ctx.fillStyle = this.legendColors[index].fill; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + ctx.fillStyle = this.legendColors[index].fill; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - },this); + },this); + } } }); @@ -1561,14 +1597,24 @@ ctx.font = this.font; each(this.yLabels,function(labelString,index){ var yLabelCenter = this.endPoint - (yLabelGap * index), - linePositionY = Math.round(yLabelCenter); + linePositionY = Math.round(yLabelCenter), + drawHorizontalLine = this.showHorizontalLines; ctx.textAlign = "right"; ctx.textBaseline = "middle"; if (this.showLabels){ ctx.fillText(labelString,xStart - 10,yLabelCenter); } - ctx.beginPath(); + + // This is X axis, so draw it + if (index === 0 && !drawHorizontalLine){ + drawHorizontalLine = true; + } + + if (drawHorizontalLine){ + ctx.beginPath(); + } + if (index > 0){ // This is a grid line in the centre, so drop that ctx.lineWidth = this.gridLineWidth; @@ -1581,10 +1627,12 @@ linePositionY += helpers.aliasPixel(ctx.lineWidth); - ctx.moveTo(xStart, linePositionY); - ctx.lineTo(this.width, linePositionY); - ctx.stroke(); - ctx.closePath(); + if(drawHorizontalLine){ + ctx.moveTo(xStart, linePositionY); + ctx.lineTo(this.width, linePositionY); + ctx.stroke(); + ctx.closePath(); + } ctx.lineWidth = this.lineWidth; ctx.strokeStyle = this.lineColor; @@ -1600,9 +1648,17 @@ var xPos = this.calculateX(index) + aliasPixel(this.lineWidth), // Check to see if line/bar here and decide where to place the line linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth), - isRotated = (this.xLabelRotation > 0); + isRotated = (this.xLabelRotation > 0), + drawVerticalLine = this.showVerticalLines; - ctx.beginPath(); + // This is Y axis, so draw it + if (index === 0 && !drawVerticalLine){ + drawVerticalLine = true; + } + + if (drawVerticalLine){ + ctx.beginPath(); + } if (index > 0){ // This is a grid line in the centre, so drop that @@ -1613,10 +1669,13 @@ ctx.lineWidth = this.lineWidth; ctx.strokeStyle = this.lineColor; } - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.startPoint - 3); - ctx.stroke(); - ctx.closePath(); + + if (drawVerticalLine){ + ctx.moveTo(linePos,this.endPoint); + ctx.lineTo(linePos,this.startPoint - 3); + ctx.stroke(); + ctx.closePath(); + } ctx.lineWidth = this.lineWidth; @@ -1964,6 +2023,12 @@ //Number - Width of the grid lines scaleGridLineWidth : 1, + //Boolean - Whether to show horizontal lines (except X axis) + scaleShowHorizontalLines: true, + + //Boolean - Whether to show vertical lines (except Y axis) + scaleShowVerticalLines: true, + //Boolean - If there is a stroke on each bar barShowStroke : true, @@ -2151,6 +2216,8 @@ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), lineWidth : this.options.scaleLineWidth, lineColor : this.options.scaleLineColor, + showHorizontalLines : this.options.scaleShowHorizontalLines, + showVerticalLines : this.options.scaleShowVerticalLines, gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0, @@ -2237,6 +2304,7 @@ }).call(this); + (function(){ "use strict"; @@ -2439,6 +2507,12 @@ //Number - Width of the grid lines scaleGridLineWidth : 1, + //Boolean - Whether to show horizontal lines (except X axis) + scaleShowHorizontalLines: true, + + //Boolean - Whether to show vertical lines (except Y axis) + scaleShowVerticalLines: true, + //Boolean - Whether the line is curved between points bezierCurve : true, @@ -2613,6 +2687,8 @@ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), lineWidth : this.options.scaleLineWidth, lineColor : this.options.scaleLineColor, + showHorizontalLines : this.options.scaleShowHorizontalLines, + showVerticalLines : this.options.scaleShowVerticalLines, gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, @@ -2787,6 +2863,7 @@ }).call(this); + (function(){ "use strict"; @@ -3377,4 +3454,4 @@ -}).call(this); +}).call(this); \ No newline at end of file diff --git a/rbkit-lib/model/heapdatamodel.cpp b/rbkit-lib/model/heapdatamodel.cpp index 9950f55..5c90911 100644 --- a/rbkit-lib/model/heapdatamodel.cpp +++ b/rbkit-lib/model/heapdatamodel.cpp @@ -17,12 +17,17 @@ QVariant HeapDataModel::data(const QModelIndex &index, int role) const return QVariant(); } - if (role != Qt::DisplayRole) { + BaseHeapItem *item = static_cast(index.internalPointer()); + switch(role) { + case Qt::DisplayRole: + return item->data(index.column()); + break; + case Qt::ToolTipRole: + return QVariant(item->leadingIdentifier()); + break; + default: return QVariant(); } - BaseHeapItem *item = static_cast(index.internalPointer()); - - return item->data(index.column()); } Qt::ItemFlags HeapDataModel::flags(const QModelIndex &index) const diff --git a/rbkit-lib/sqlconnectionpool.cpp b/rbkit-lib/sqlconnectionpool.cpp index 5d92c2a..a156116 100644 --- a/rbkit-lib/sqlconnectionpool.cpp +++ b/rbkit-lib/sqlconnectionpool.cpp @@ -28,7 +28,8 @@ void SqlConnectionPool::setupDatabase() file.remove(); } database = QSqlDatabase::addDatabase("QSQLITE"); - database.setDatabaseName(":memory:"); + database.setDatabaseName("/tmp/rbkit.db"); + currentVersion = 0; if (!database.open()) { qDebug() << query.lastError(); @@ -38,6 +39,12 @@ void SqlConnectionPool::setupDatabase() qDebug() << "Setting up database done"; } +void SqlConnectionPool::closeDatabase() +{ + database.close(); + database.removeDatabase("/tmp/rbkit.db"); +} + void SqlConnectionPool::prepareTables() { QVector objectCreation; diff --git a/rbkit-lib/sqlconnectionpool.h b/rbkit-lib/sqlconnectionpool.h index 815f314..533d97a 100644 --- a/rbkit-lib/sqlconnectionpool.h +++ b/rbkit-lib/sqlconnectionpool.h @@ -25,6 +25,7 @@ class SqlConnectionPool public: static SqlConnectionPool* getInstance(); void setupDatabase(); + void closeDatabase(); void prepareTables(); void loadSnapshot(ObjectStore *objectStore); HeapItem *rootOfSnapshot(int snapShotVersion); diff --git a/rbkit-lib/subscriber.cpp b/rbkit-lib/subscriber.cpp index 430e206..674cf60 100644 --- a/rbkit-lib/subscriber.cpp +++ b/rbkit-lib/subscriber.cpp @@ -136,6 +136,9 @@ void Subscriber::stop() commandSocket->sendCommand(stopCmd); } objectStore->reset(); + static const QString eventName("disconnected"); + QVariantMap map; + jsBridge->sendMapToJs(eventName, QDateTime(), map); qDebug() << "stopped"; } diff --git a/rbkit-lib/ui/rbkitmainwindow.cpp b/rbkit-lib/ui/rbkitmainwindow.cpp index 859dd65..c16cc8e 100644 --- a/rbkit-lib/ui/rbkitmainwindow.cpp +++ b/rbkit-lib/ui/rbkitmainwindow.cpp @@ -14,7 +14,6 @@ RbkitMainWindow::RbkitMainWindow(QWidget *parent) : ui(new Ui::RbkitMainWindow) { Q_INIT_RESOURCE(tool_icons); - RBKit::SqlConnectionPool::getInstance()->setupDatabase(); ui->setupUi(this); actionToolbar = new ActionToolbar(ui); setupToolbarStyle(); @@ -177,12 +176,16 @@ void RbkitMainWindow::disconnectedFromSocket() ui->statusbar->showMessage("Not connected to any Ruby application"); actionToolbar->disableProfileActions(); memoryView->processDetail->disconnectedFromProcess(); + snapShotState->reset(); + RBKit::SqlConnectionPool::getInstance()->closeDatabase(); + RBKit::SqlConnectionPool::getInstance()->setupDatabase(); connectionInProgress = false; } void RbkitMainWindow::connectedToSocket() { + RBKit::SqlConnectionPool::getInstance()->setupDatabase(); actionToolbar->enableProfileActions(); ui->action_Connect->setText(tr("&Disconnect")); ui->action_Connect->setIcon(QIcon(":/icons/disconnect-32.png"));