Add API Inspector

This commit is contained in:
baldurk
2016-11-21 19:33:19 +01:00
parent ad3ea6513d
commit e13b6277ed
10 changed files with 442 additions and 4 deletions
+17
View File
@@ -33,6 +33,7 @@
#include <QProgressDialog>
#include <QStandardPaths>
#include <QTimer>
#include "Windows/APIInspector.h"
#include "Windows/Dialogs/CaptureDialog.h"
#include "Windows/Dialogs/LiveCapture.h"
#include "Windows/EventBrowser.h"
@@ -429,6 +430,18 @@ EventBrowser *CaptureContext::eventBrowser()
return m_EventBrowser;
}
APIInspector *CaptureContext::apiInspector()
{
if(m_APIInspector)
return m_APIInspector;
m_APIInspector = new APIInspector(this, m_MainWindow);
m_APIInspector->setObjectName("apiInspector");
m_APIInspector->setWindowIcon(*m_Icon);
return m_APIInspector;
}
TextureViewer *CaptureContext::textureViewer()
{
if(m_TextureViewer)
@@ -471,6 +484,10 @@ QWidget *CaptureContext::createToolWindow(const QString &objectName)
{
return eventBrowser();
}
else if(objectName == "apiInspector")
{
return apiInspector();
}
else if(objectName == "capDialog")
{
return captureDialog();
+4
View File
@@ -47,6 +47,7 @@ struct ILogViewerForm
class MainWindow;
class EventBrowser;
class APIInspector;
class TextureViewer;
class CaptureDialog;
class QProgressDialog;
@@ -126,10 +127,12 @@ public:
const QIcon &winIcon() { return *m_Icon; }
MainWindow *mainWindow() { return m_MainWindow; }
EventBrowser *eventBrowser();
APIInspector *apiInspector();
TextureViewer *textureViewer();
CaptureDialog *captureDialog();
bool hasEventBrowser() { return m_EventBrowser != NULL; }
bool hasAPIInspector() { return m_APIInspector != NULL; }
bool hasTextureViewer() { return m_TextureViewer != NULL; }
bool hasCaptureDialog() { return m_CaptureDialog != NULL; }
QWidget *createToolWindow(const QString &objectName);
@@ -197,6 +200,7 @@ private:
QProgressDialog *m_Progress;
MainWindow *m_MainWindow = NULL;
EventBrowser *m_EventBrowser = NULL;
APIInspector *m_APIInspector = NULL;
TextureViewer *m_TextureViewer = NULL;
CaptureDialog *m_CaptureDialog = NULL;
};
+166
View File
@@ -0,0 +1,166 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2016 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "APIInspector.h"
#include <QRegularExpression>
#include "ui_APIInspector.h"
Q_DECLARE_METATYPE(FetchAPIEvent);
APIInspector::APIInspector(CaptureContext *ctx, QWidget *parent)
: QFrame(parent), ui(new Ui::APIInspector), m_Ctx(ctx)
{
ui->setupUi(this);
ui->apiEvents->headerItem()->setText(0, "EID");
ui->apiEvents->headerItem()->setText(1, "Event");
ui->splitter->setCollapsible(1, true);
ui->splitter->setSizes({1, 0});
m_Ctx->AddLogViewer(this);
}
APIInspector::~APIInspector()
{
m_Ctx->windowClosed(this);
m_Ctx->RemoveLogViewer(this);
delete ui;
}
void APIInspector::OnLogfileLoaded()
{
}
void APIInspector::OnLogfileClosed()
{
ui->apiEvents->clear();
ui->callstack->clear();
}
void APIInspector::OnEventSelected(uint32_t eventID)
{
ui->apiEvents->clearSelection();
fillAPIView();
}
void APIInspector::addCallstack(rdctype::array<rdctype::str> calls)
{
ui->callstack->setUpdatesEnabled(false);
ui->callstack->clear();
if(calls.count == 1 && calls[0].count == 0)
{
ui->callstack->addItem(tr("Symbols not loaded. Tools -> Resolve Symbols."));
}
else
{
for(rdctype::str &s : calls)
ui->callstack->addItem(ToQStr(s));
}
ui->callstack->setUpdatesEnabled(true);
}
void APIInspector::on_apiEvents_itemSelectionChanged()
{
if(ui->apiEvents->selectedItems().count() == 0)
return;
FetchAPIEvent ev = ui->apiEvents->selectedItems()[0]->data(0, Qt::UserRole).value<FetchAPIEvent>();
if(ev.callstack.count > 0)
{
m_Ctx->Renderer()->AsyncInvoke([this, ev](IReplayRenderer *r) {
rdctype::array<rdctype::str> trace;
r->GetResolve(ev.callstack.elems, ev.callstack.count, &trace);
GUIInvoke::call([this, trace]() { addCallstack(trace); });
});
}
else
{
ui->callstack->setUpdatesEnabled(false);
ui->callstack->clear();
ui->callstack->addItem(tr("No Callstack available."));
ui->callstack->setUpdatesEnabled(true);
}
}
void APIInspector::fillAPIView()
{
ui->apiEvents->setUpdatesEnabled(false);
ui->apiEvents->clear();
QRegularExpression rgxopen("^\\s*{");
QRegularExpression rgxclose("^\\s*}");
const FetchDrawcall *draw = m_Ctx->CurDrawcall();
if(draw != NULL && draw->events.count > 0)
{
int e = 0;
for(const FetchAPIEvent &ev : draw->events)
{
QStringList lines = ToQStr(ev.eventDesc).split("\n", QString::SkipEmptyParts);
QTreeWidgetItem *root =
new QTreeWidgetItem(ui->apiEvents, QStringList{QString::number(ev.eventID), lines[0]});
int i = 1;
if(i < lines.count() && lines[i].trimmed() == "{")
i++;
QList<QTreeWidgetItem *> nodestack;
nodestack.push_back(root);
for(; i < lines.count(); i++)
{
if(rgxopen.match(lines[i]).hasMatch())
nodestack.push_back(nodestack.back()->child(nodestack.back()->childCount() - 1));
else if(rgxclose.match(lines[i]).hasMatch())
nodestack.pop_back();
else if(!nodestack.empty())
new QTreeWidgetItem(nodestack.back(), QStringList{"", lines[i].trimmed()});
}
if(ev.eventID == draw->eventID)
{
QFont font = root->font(1);
font.setBold(true);
root->setFont(1, font);
}
root->setData(0, Qt::UserRole, QVariant::fromValue(ev));
ui->apiEvents->insertTopLevelItem(e, root);
e++;
ui->apiEvents->clearSelection();
ui->apiEvents->setItemSelected(root, true);
}
}
ui->apiEvents->setUpdatesEnabled(true);
}
+56
View File
@@ -0,0 +1,56 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2016 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <QFrame>
#include "Code/CaptureContext.h"
namespace Ui
{
class APIInspector;
}
class APIInspector : public QFrame, public ILogViewerForm
{
Q_OBJECT
public:
explicit APIInspector(CaptureContext *ctx, QWidget *parent = 0);
~APIInspector();
void OnLogfileLoaded();
void OnLogfileClosed();
void OnEventSelected(uint32_t eventID);
public slots:
void on_apiEvents_itemSelectionChanged();
private:
Ui::APIInspector *ui;
CaptureContext *m_Ctx = NULL;
void addCallstack(rdctype::array<rdctype::str> calls);
void fillAPIView();
};
+90
View File
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>APIInspector</class>
<widget class="QFrame" name="APIInspector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>434</width>
<height>425</height>
</rect>
</property>
<property name="windowTitle">
<string>API Inspector</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="handleWidth">
<number>16</number>
</property>
<widget class="QTreeWidget" name="apiEvents">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>false</bool>
</property>
<property name="indentation">
<number>20</number>
</property>
<property name="rootIsDecorated">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>true</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
</widget>
<widget class="QListWidget" name="callstack">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+80
View File
@@ -27,12 +27,14 @@
#include <QFileInfo>
#include <QMimeData>
#include <QProgressBar>
#include <QProgressDialog>
#include "Code/CaptureContext.h"
#include "Code/QRDUtils.h"
#include "Resources/resource.h"
#include "Windows/Dialogs/AboutDialog.h"
#include "Windows/Dialogs/CaptureDialog.h"
#include "Windows/Dialogs/LiveCapture.h"
#include "APIInspector.h"
#include "EventBrowser.h"
#include "TextureViewer.h"
#include "ui_MainWindow.h"
@@ -126,6 +128,9 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
return m_Ctx->createToolWindow(objectName);
});
ui->action_Resolve_Symbols->setEnabled(false);
ui->action_Resolve_Symbols->setText(tr("Resolve Symbols"));
bool loaded = LoadLayout(0);
// create default layout if layout failed to load
@@ -147,6 +152,13 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
ui->toolWindowManager->addToolWindow(
capDialog, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->toolWindowManager->areaOf(textureViewer)));
APIInspector *apiInspector = m_Ctx->apiInspector();
ui->toolWindowManager->addToolWindow(
apiInspector,
ToolWindowManager::AreaReference(ToolWindowManager::BottomOf,
ui->toolWindowManager->areaOf(eventBrowser), 0.3f));
}
m_Ctx->AddLogViewer(this);
@@ -917,6 +929,29 @@ void MainWindow::on_action_Mesh_Output_triggered()
// TODO Mesh view
}
void MainWindow::on_action_API_Inspector_triggered()
{
APIInspector *apiInspector = m_Ctx->apiInspector();
if(ui->toolWindowManager->toolWindows().contains(apiInspector))
{
ToolWindowManager::raiseToolWindow(apiInspector);
}
else
{
if(m_Ctx->hasEventBrowser())
{
ToolWindowManager::AreaReference ref(ToolWindowManager::BottomOf,
ui->toolWindowManager->areaOf(m_Ctx->eventBrowser()));
ui->toolWindowManager->addToolWindow(apiInspector, ref);
}
else
{
ui->toolWindowManager->addToolWindow(apiInspector, leftToolArea());
}
}
}
void MainWindow::on_action_Event_Browser_triggered()
{
EventBrowser *eventBrowser = m_Ctx->eventBrowser();
@@ -961,6 +996,51 @@ void MainWindow::on_action_Inject_into_Process_triggered()
ui->toolWindowManager->addToolWindow(capDialog, mainToolArea());
}
void MainWindow::on_action_Resolve_Symbols_triggered()
{
m_Ctx->Renderer()->AsyncInvoke([this](IReplayRenderer *r) { r->InitResolver(); });
QProgressDialog *m_Progress =
new QProgressDialog(tr("Please Wait - Resolving Symbols"), QString(), 0, 0, this);
m_Progress->setWindowTitle("Please Wait");
m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint);
m_Progress->setWindowIcon(QIcon());
m_Progress->setMinimumSize(QSize(250, 0));
m_Progress->setMaximumSize(QSize(250, 10000));
m_Progress->setCancelButton(NULL);
m_Progress->setMinimumDuration(0);
m_Progress->setWindowModality(Qt::ApplicationModal);
m_Progress->setValue(0);
QLabel *label = new QLabel(m_Progress);
label->setText(tr("Please Wait - Resolving Symbols"));
label->setAlignment(Qt::AlignCenter);
label->setWordWrap(true);
m_Progress->setLabel(label);
LambdaThread *thread = new LambdaThread([this, m_Progress]() {
bool running = true;
while(running)
{
// just bail if we managed to get here without a resolver.
m_Ctx->Renderer()->BlockInvoke(
[&running](IReplayRenderer *r) { running = r->HasCallstacks() && !r->InitResolver(); });
}
GUIInvoke::call([this, m_Progress]() {
m_Progress->hide();
delete m_Progress;
if(m_Ctx->hasAPIInspector())
m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged();
});
});
thread->selfDelete(true);
thread->start();
}
void MainWindow::saveLayout_triggered()
{
LoadSaveLayout(qobject_cast<QAction *>(QObject::sender()), true);
+2
View File
@@ -78,10 +78,12 @@ private slots:
void on_action_Open_Log_triggered();
void on_action_Close_Log_triggered();
void on_action_Mesh_Output_triggered();
void on_action_API_Inspector_triggered();
void on_action_Event_Browser_triggered();
void on_action_Texture_Viewer_triggered();
void on_action_Capture_Log_triggered();
void on_action_Inject_into_Process_triggered();
void on_action_Resolve_Symbols_triggered();
// manual slots
void saveLayout_triggered();
+6 -3
View File
@@ -110,7 +110,8 @@ SOURCES += 3rdparty/toolwindowmanager/ToolWindowManager.cpp \
Windows/Dialogs/CaptureDialog.cpp \
Code/QRDUtils.cpp \
Windows/Dialogs/LiveCapture.cpp \
Widgets/Extended/RDListWidget.cpp
Widgets/Extended/RDListWidget.cpp \
Windows/APIInspector.cpp
HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \
3rdparty/toolwindowmanager/ToolWindowManagerArea.h \
@@ -138,7 +139,8 @@ HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \
Windows/Dialogs/CaptureDialog.h \
Code/QRDUtils.h \
Windows/Dialogs/LiveCapture.h \
Widgets/Extended/RDListWidget.h
Widgets/Extended/RDListWidget.h \
Windows/APIInspector.h
FORMS += Windows/Dialogs/AboutDialog.ui \
Windows/MainWindow.ui \
@@ -148,7 +150,8 @@ FORMS += Windows/Dialogs/AboutDialog.ui \
Widgets/ThumbnailStrip.ui \
Windows/Dialogs/TextureSaveDialog.ui \
Windows/Dialogs/CaptureDialog.ui \
Windows/Dialogs/LiveCapture.ui
Windows/Dialogs/LiveCapture.ui \
Windows/APIInspector.ui
RESOURCES += \
resources.qrc
+6 -1
View File
@@ -285,6 +285,7 @@
<ClCompile Include="Code\qprocessinfo.cpp" />
<ClCompile Include="Code\QRDUtils.cpp" />
<ClCompile Include="generated\moc_AboutDialog.cpp" />
<ClCompile Include="generated\moc_APIInspector.cpp" />
<ClCompile Include="generated\moc_CaptureDialog.cpp" />
<ClCompile Include="generated\moc_CustomPaintWidget.cpp" />
<ClCompile Include="generated\moc_EventBrowser.cpp" />
@@ -316,6 +317,7 @@
<ClCompile Include="Widgets\ThumbnailStrip.cpp" />
<ClCompile Include="Code\CaptureContext.cpp" />
<ClCompile Include="Widgets\CustomPaintWidget.cpp" />
<ClCompile Include="Windows\APIInspector.cpp" />
<ClCompile Include="Windows\Dialogs\AboutDialog.cpp" />
<ClCompile Include="Windows\Dialogs\CaptureDialog.cpp" />
<ClCompile Include="Windows\Dialogs\LiveCapture.cpp" />
@@ -345,6 +347,7 @@
<ClInclude Include="Code\qprocessinfo.h" />
<ClInclude Include="Code\QRDUtils.h" />
<ClInclude Include="generated\ui_AboutDialog.h" />
<ClInclude Include="generated\ui_APIInspector.h" />
<ClInclude Include="generated\ui_CaptureDialog.h" />
<ClInclude Include="generated\ui_EventBrowser.h" />
<ClInclude Include="generated\ui_LiveCapture.h" />
@@ -364,6 +367,7 @@
<ClInclude Include="Widgets\ThumbnailStrip.h" />
<ClInclude Include="Code\CaptureContext.h" />
<ClInclude Include="Widgets\CustomPaintWidget.h" />
<ClInclude Include="Windows\APIInspector.h" />
<ClInclude Include="Windows\Dialogs\AboutDialog.h" />
<ClInclude Include="Windows\Dialogs\CaptureDialog.h" />
<ClInclude Include="Windows\Dialogs\LiveCapture.h" />
@@ -381,6 +385,7 @@
<ItemGroup>
<None Include="Widgets\ResourcePreview.ui" />
<None Include="Widgets\ThumbnailStrip.ui" />
<None Include="Windows\APIInspector.ui" />
<None Include="Windows\Dialogs\AboutDialog.ui" />
<None Include="Windows\Dialogs\CaptureDialog.ui" />
<None Include="Windows\Dialogs\LiveCapture.ui" />
@@ -510,4 +515,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>
@@ -193,6 +193,12 @@
<ClCompile Include="generated\moc_RDListWidget.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="generated\moc_APIInspector.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="Windows\APIInspector.cpp">
<Filter>Windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
@@ -294,6 +300,12 @@
<ClInclude Include="Widgets\Extended\RDListWidget.h">
<Filter>Widgets\Extended</Filter>
</ClInclude>
<ClInclude Include="generated\ui_APIInspector.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="Windows\APIInspector.h">
<Filter>Windows</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="resources.qrc">
@@ -500,6 +512,9 @@
<None Include="Windows\Dialogs\LiveCapture.ui">
<Filter>Windows\Dialogs</Filter>
</None>
<None Include="Windows\APIInspector.ui">
<Filter>Windows</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="generated\ui_AboutDialog.h">