Commit f774556d authored by Christiaan Janssen's avatar Christiaan Janssen

QmlProfiler: first commit

parent 39daf232
......@@ -101,7 +101,7 @@ using namespace Analyzer::Internal;
// A separate 'Analzye' mode is not used in Qt Creator 2.2.
// Consider re-introducing it if a real use case for a separate main window with docks
// appears.
enum { useAnalyzeMode = 0 };
enum { useAnalyzeMode = 1 };
namespace Analyzer {
namespace Internal {
......
......@@ -131,6 +131,7 @@ AnalyzerRunControl::~AnalyzerRunControl()
{
if (m_isRunning)
stop();
delete m_engine;
}
void AnalyzerRunControl::start()
......
......@@ -38,6 +38,7 @@ SUBDIRS = plugin_coreplugin \
plugin_classview \
plugin_tasklist \
plugin_analyzerbase \
plugin_qmlprofiler \
plugin_qmljstools \
plugin_macros \
debugger/dumper.pro
......@@ -263,6 +264,10 @@ plugin_analyzerbase.depends += plugin_projectexplorer
plugin_memcheck.depends += plugin_valgrindtoolbase
}
plugin_qmlprofiler.subdir = qmlprofiler
plugin_qmlprofiler.depends = plugin_coreplugin
plugin_qmlprofiler.depends = plugin_analyzerbase
plugin_qmljstools.subdir = qmljstools
plugin_qmljstools.depends = plugin_projectexplorer
plugin_qmljstools.depends += plugin_coreplugin
......
import QtQuick 1.1
import Monitor 1.0
import "MainView.js" as Plotter
Item {
id: detail
property string label
property string content
property int maxLines: 4
signal linkActivated(string url)
height: childrenRect.height
width: childrenRect.width
Item {
id: guideline
x: 70
width: 5
}
Text {
id: lbl
text: label + ":"
font.pixelSize: 12
font.bold: true
anchors.right: guideline.left
}
Text {
text: content
font.pixelSize: 12
anchors.baseline: lbl.baseline
anchors.left: guideline.right
maximumLineCount: maxLines
onLinkActivated: detail.linkActivated(link)
}
}
import QtQuick 1.1
import Monitor 1.0
import "MainView.js" as Plotter
Text {
id: elapsed
color: "white"
Timer {
property date startDate
property bool reset: true
running: connection.recording
repeat: true
onRunningChanged: if (running) reset = true
interval: 100
triggeredOnStart: true
onTriggered: {
if (reset) {
startDate = new Date()
reset = false
}
var time = (new Date() - startDate)/1000
elapsed.text = time.toFixed(1) + "s"
}
}
}
import QtQuick 1.1
Item {
property alias text: txt.text
height: 50
width: 150 //### required, or ignored by positioner
Text {
id: txt;
x: 5
font.pixelSize: 12
color: "#232323"
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
height: 1
width: parent.width
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
.pragma library
var values = [ ]; //events
var ranges = [ ];
var frameFps = [ ];
var valuesdone = false;
var xmargin = 0;
var ymargin = 0;
var names = [ "Painting", "Compiling", "Creating", "Binding", "Handling Signal"]
//### need better way to manipulate color from QML. In the meantime, these need to be kept in sync.
var colors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ];
var origColors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ];
var xRayColors = [ "#6699CCB3", "#6699CCCC", "#6699B3CC", "#669999CC", "#66CC99B3", "#66CC99CC", "#66CCCC99", "#66CCB399" ];
function reset()
{
values = [];
ranges = [];
frameFps = [];
xmargin = 0;
ymargin = 0;
valuesdone = false;
}
function calcFps()
{
if (values.length)
frameFps = new Array(values.length - 1);
for (var i = 0; i < values.length - 1; ++i) {
var frameTime = (values[i + 1] - values[i]) / 1000000;
frameFps[i] = 1000 / frameTime;
}
}
//draw background of the graph
function drawGraph(canvas, ctxt, region)
{
var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height);
grad.addColorStop(0, '#fff');
grad.addColorStop(1, '#ccc');
ctxt.fillStyle = grad;
ctxt.fillRect(0, 0, canvas.canvasSize.width + xmargin, canvas.canvasSize.height - ymargin);
}
//draw the actual data to be graphed
function drawData(canvas, ctxt, region)
{
if (values.length == 0 && ranges.length == 0)
return;
var width = canvas.canvasSize.width - xmargin;
var height = canvas.height - ymargin;
var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
var spacing = width / sumValue;
//### only draw those in range
for (var ii = 0; ii < ranges.length; ++ii) {
var xx = (ranges[ii].start - ranges[0].start) * spacing + xmargin;
if (xx > region.x + region.width)
continue;
var size = ranges[ii].duration * spacing;
if (xx + size < region.x)
continue;
if (size < 0.5)
size = 0.5;
ctxt.fillStyle = "rgba(0,0,0,1)" //colors[ranges[ii].type];
ctxt.fillRect(xx, ranges[ii].type*10, size, 10);
}
//draw fps overlay
var heightScale = height / 60;
ctxt.beginPath();
ctxt.moveTo(0,0);
for (var i = 1; i < values.length; ++i) {
var xx = (values[i] - ranges[0].start) * spacing + xmargin;
ctxt.lineTo(xx, height - frameFps[i-1]*heightScale)
}
ctxt.lineTo(width, 0);
ctxt.closePath();
var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height);
grad.addColorStop(0, "rgba(255,128,128,.5)");
grad.addColorStop(1, "rgba(255,0,0,.5)");
ctxt.fillStyle = grad;
ctxt.fill();
}
function plot(canvas, ctxt, region)
{
drawGraph(canvas, ctxt, region);
drawData(canvas, ctxt, region);
}
function xScale(canvas)
{
if (values.length === 0 && ranges.length === 0)
return;
var width = canvas.canvasSize.width - xmargin;
var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
var spacing = sumValue / width;
return spacing;
}
import QtQuick 1.1
import Monitor 1.0
import "MainView.js" as Plotter
Rectangle {
id: root
property variant colors: Plotter.colors //the colors used for the timeline data
property bool xRay: false //useful for seeing "nested" ranges (but redraw is buggy -- QGV problem?)
property Item currentItem //currently selected item in the view
property bool zooming:false
// move the cursor in the editor
signal updateCursorPosition
property string fileName: ""
property int lineNumber: -1
function gotoSourceLocation(file,line) {
root.fileName = file;
root.lineNumber = line;
root.updateCursorPosition();
}
//handle debug data coming from C++
Connections {
target: connection
onEvent: {
if (Plotter.valuesdone) {
Plotter.reset();
view.clearData();
rangeMover.x = 2
rangeMover.opacity = 0
}
if (!Plotter.valuesdone && event === 0) //### only handle paint event
Plotter.values.push(time);
}
onRange: {
if (Plotter.valuesdone) {
Plotter.reset();
view.clearData();
rangeMover.x = 2
rangeMover.opacity = 0
}
if (!Plotter.valuesdone)
Plotter.ranges.push( { type: type, start: startTime, duration: length, label: data, fileName: fileName, line: line } );
}
onComplete: {
Plotter.valuesdone = true;
Plotter.calcFps();
view.setRanges(Plotter.ranges);
view.updateTimeline();
canvas.requestPaint();
rangeMover.x = 1 //### hack to get view to display things immediately
rangeMover.opacity = 1
}
}
//timeline background
Item {
anchors.fill: flick
Column {
anchors.fill: parent
Repeater {
model: 5 //### values.length?
delegate: Rectangle {
width: parent.width
height: 50 //###
color: index % 2 ? "#fafafa" : "white"
}
}
}
}
//our main interaction view
Flickable {
id: flick
anchors.top: parent.top
anchors.right: parent.right
anchors.left: labels.right
anchors.bottom: canvas.top
contentWidth: view.totalWidth
contentHeight: view.height
TimelineView {
id: view
width: flick.width; height: flick.height
startX: flick.contentX
onStartXChanged: {
var newX = startTime / Plotter.xScale(canvas) - canvas.canvasWindow.x;
if (Math.abs(rangeMover.x - newX) > .01)
rangeMover.x = newX
if (Math.abs(startX - flick.contentX) > .01)
flick.contentX = startX
}
startTime: rangeMover.value
property real prevXStep: -1
property real possibleEndTime: startTime + (rangeMover.width*Plotter.xScale(canvas))
onPossibleEndTimeChanged: {
var set = ((zooming && prevXStep != canvas.canvasWindow.x) || !zooming);
prevXStep = canvas.canvasWindow.x;
if (set)
endTime = possibleEndTime
}
onEndTimeChanged: updateTimeline()
delegate: Rectangle {
id: obj
property color baseColor: colors[type]
property color myColor: baseColor
function conditionalHide() {
if (!mouseArea.containsMouse)
mouseArea.exited()
}
height: 50
gradient: Gradient {
GradientStop { position: 0.0; color: myColor }
GradientStop { position: 0.5; color: Qt.darker(myColor, 1.1) }
GradientStop { position: 1.0; color: myColor }
}
smooth: true
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
currentItem = obj
myColor = Qt.darker(baseColor, 1.2)
rangeDetails.duration = duration
rangeDetails.label = label
rangeDetails.file = fileName
rangeDetails.line = line
rangeDetails.type = Plotter.names[type]
var pos = mapToItem(rangeDetails.parent, mouseX, y+height)
var preferredX = Math.max(10, pos.x - rangeDetails.width/2)
if (preferredX + rangeDetails.width > rangeDetails.parent.width)
preferredX = rangeDetails.parent.width - rangeDetails.width
rangeDetails.x = preferredX
rangeDetails.y = pos.y + 10
rangeDetails.visible = true
}
onExited: {
myColor = baseColor
rangeDetails.visible = false
rangeDetails.duration = ""
rangeDetails.label = ""
rangeDetails.type = ""
rangeDetails.file = ""
rangeDetails.line = -1
}
onClicked: root.gotoSourceLocation(rangeDetails.file, rangeDetails.line);
}
}
}
}
//popup showing the details for the hovered range
RangeDetails {
id: rangeDetails
}
Rectangle {
id: labels
width: 150
color: "#dcdcdc"
anchors.top: root.top
anchors.bottom: canvas.top
Column {
//### change to use Repeater + Plotter.names?
Label { text: "Painting" }
Label { text: "Compiling" }
Label { text: "Creating" }
Label { text: "Binding" }
Label { text: "Signal Handler" }
}
//right border divider
Rectangle {
width: 1
height: parent.height
anchors.right: parent.right
color: "#cccccc"
}
}
//bottom border divider
Rectangle {
height: 1
width: parent.width
anchors.bottom: canvas.top
color: "#cccccc"
}
//"overview" graph at the bottom
TiledCanvas {
id: canvas
anchors.bottom: parent.bottom
width: parent.width; height: 50
property int canvasWidth: width
canvasSize {
width: canvasWidth
height: canvas.height
}
tileSize.width: width
tileSize.height: height
canvasWindow.width: width
canvasWindow.height: 50
onDrawRegion: {
if (Plotter.valuesdone)
Plotter.plot(canvas, ctxt, region);
else
Plotter.drawGraph(canvas, ctxt, region) //just draw the background
}
}
//moves the range mover to the position of a click
MouseArea {
anchors.fill: canvas
//### ideally we could press to position then immediately drag
onPressed: rangeMover.x = mouse.x - rangeMover.width/2
}
RangeMover {
id: rangeMover
opacity: 0
anchors.top: canvas.top
}
Rectangle {
width: 50
height: 30
anchors.right: root.right
anchors.top: root.top
radius: 4
color: "#606085"
Elapsed {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
}
}
}
<plugin name=\"QmlProfiler\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2011 Nokia Corporation</copyright>
<license>
Commercial Usage
Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia.
GNU Lesser General Public License Usage
Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
</license>
<description>Qml Profiler Plugin</description>
<url>http://qt.nokia.com</url>
<dependencyList>
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
<dependency name=\"AnalyzerBase\" version=\"$$QTCREATOR_VERSION\"/>
</dependencyList>
</plugin>
import QtQuick 1.1
import Monitor 1.0
import "MainView.js" as Plotter
BorderImage {
id: rangeDetails
property string duration //###int?
property string label
property string type
property string file
property int line
source: "popup.png"
border {
left: 10; top: 10
right: 20; bottom: 20
}
width: col.width + 45
height: childrenRect.height + 30
z: 1
visible: false
//title
Text {
id: typeTitle
text: rangeDetails.type
font.bold: true
y: 10
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: -5
}
//details
Column {
id: col
anchors.top: typeTitle.bottom
Detail {
label: "Duration"
content: rangeDetails.duration + "μs"
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: "Details"
content: rangeDetails.label
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: "Location"
content: {
var file = rangeDetails.file
var pos = file.lastIndexOf("/")
if (pos != -1)
file = file.substr(pos+1)
return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
}
onLinkActivated: Qt.openUrlExternally(url)
}
}
}
import "MainView.js" as Plotter
import QtQuick 1.1
import Monitor 1.0
Item {
id: rangeMover
width: rect.width; height: 50