Highlight Breakpoints and PC in decompiler (#1948)

This commit is contained in:
Itay Cohen 2019-12-19 19:58:30 +02:00 committed by Florian Märkl
parent 58b44893f9
commit 4e84d1b502
6 changed files with 157 additions and 2 deletions

View File

@ -35,13 +35,32 @@ QList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textE
return selections; return selections;
} }
QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor)
QTextEdit::ExtraSelection createLineHighlight(const QTextCursor &cursor, QColor highlightColor)
{ {
QColor highlightColor = ConfigColor("lineHighlight");
QTextEdit::ExtraSelection highlightSelection; QTextEdit::ExtraSelection highlightSelection;
highlightSelection.cursor = cursor; highlightSelection.cursor = cursor;
highlightSelection.format.setBackground(highlightColor); highlightSelection.format.setBackground(highlightColor);
highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true); highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true);
highlightSelection.cursor.clearSelection(); highlightSelection.cursor.clearSelection();
return highlightSelection; return highlightSelection;
}
QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor)
{
QColor highlightColor = ConfigColor("lineHighlight");
return createLineHighlight(cursor, highlightColor);
}
QTextEdit::ExtraSelection createLineHighlightPC(const QTextCursor &cursor)
{
QColor highlightColor = ConfigColor("highlightPC");
return createLineHighlight(cursor, highlightColor);
}
QTextEdit::ExtraSelection createLineHighlightBP(const QTextCursor &cursor)
{
QColor highlightColor = ConfigColor("gui.breakpoint_background");
return createLineHighlight(cursor, highlightColor);
} }

View File

@ -6,7 +6,41 @@
class QPlainTextEdit; class QPlainTextEdit;
class QString; class QString;
/**
* @brief createSameWordsSelections se
* @param textEdit
* @param word
* @return
*/
QList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit, const QString &word); QList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit, const QString &word);
/**
* @brief createLineHighlight
* @param cursor - a Cursor object represents the line to be highlighted
* @param highlightColor - the color to be used for highlighting. The color is decided by the callee for different usages (BP, PC, Current line, ...)
* @return ExtraSelection with highlighted line
*/
QTextEdit::ExtraSelection createLineHighlight(const QTextCursor &cursor, QColor highlightColor);
/**
* @brief This function responsible to highlight the currently selected line
* @param cursor - a Cursor object represents the line to be highlighted
* @return ExtraSelection with highlighted line
*/
QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor); QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor);
/**
* @brief This function responsible to highlight the program counter line
* @param cursor - a Cursor object represents the line to be highlighted
* @return ExtraSelection with highlighted line
*/
QTextEdit::ExtraSelection createLineHighlightPC(const QTextCursor &cursor);
/**
* @brief This function responsible to highlight a line with breakpoint
* @param cursor - a Cursor object represents the line to be highlighted
* @return ExtraSelection with highlighted line
*/
QTextEdit::ExtraSelection createLineHighlightBP(const QTextCursor &cursor);
#endif //CUTTER_SELECTIONHIGHLIGHT_H #endif //CUTTER_SELECTIONHIGHLIGHT_H

View File

@ -1802,6 +1802,19 @@ QList<RVA> CutterCore::getBreakpointsAddresses()
return bpAddresses; return bpAddresses;
} }
QList<RVA> CutterCore::getBreakpointsInFunction(RVA funcAddr)
{
QList<RVA> allBreakpoints = getBreakpointsAddresses();
QList<RVA> functionBreakpoints;
// Use std manipulations to take only the breakpoints that belong to this function
std::copy_if(allBreakpoints.begin(),
allBreakpoints.end(),
std::back_inserter(functionBreakpoints),
[this, funcAddr](RVA BPadd) { return getFunctionStart(BPadd) == funcAddr; });
return functionBreakpoints;
}
bool CutterCore::isBreakpoint(const QList<RVA> &breakpoints, RVA addr) bool CutterCore::isBreakpoint(const QList<RVA> &breakpoints, RVA addr)
{ {
return breakpoints.contains(addr); return breakpoints.contains(addr);

View File

@ -327,6 +327,11 @@ public:
void disableBreakpoint(RVA addr); void disableBreakpoint(RVA addr);
bool isBreakpoint(const QList<RVA> &breakpoints, RVA addr); bool isBreakpoint(const QList<RVA> &breakpoints, RVA addr);
QList<RVA> getBreakpointsAddresses(); QList<RVA> getBreakpointsAddresses();
/**
* @brief Get all breakpoinst that are belong to a functions at this address
*/
QList<RVA> getBreakpointsInFunction(RVA funcAddr);
QString getActiveDebugPlugin(); QString getActiveDebugPlugin();
QStringList getDebugPlugins(); QStringList getDebugPlugins();
void setDebugPlugin(QString plugin); void setDebugPlugin(QString plugin);

View File

@ -32,6 +32,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main, QAction *action) :
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
connect(Core(), SIGNAL(registersChanged()), this, SLOT(highlightPC()));
decompiledFunctionAddr = RVA_INVALID; decompiledFunctionAddr = RVA_INVALID;
decompilerWasBusy = false; decompilerWasBusy = false;
@ -158,6 +159,8 @@ void DecompilerWidget::doRefresh(RVA addr)
return; return;
} }
// Clear all selections since we just refreshed
ui->textEdit->setExtraSelections({});
decompiledFunctionAddr = Core()->getFunctionStart(addr); decompiledFunctionAddr = Core()->getFunctionStart(addr);
dec->decompileAt(addr); dec->decompileAt(addr);
if (dec->isRunning()) { if (dec->isRunning()) {
@ -173,6 +176,18 @@ void DecompilerWidget::refreshDecompiler()
doRefresh(); doRefresh();
} }
QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
{
size_t pos = code.PositionForOffset(addr);
if (pos == SIZE_MAX || pos == 0) {
return QTextCursor();
}
QTextCursor cursor = ui->textEdit->textCursor();
cursor.setPosition(pos);
return cursor;
}
void DecompilerWidget::decompilationFinished(AnnotatedCode code) void DecompilerWidget::decompilationFinished(AnnotatedCode code)
{ {
ui->progressLabel->setVisible(false); ui->progressLabel->setVisible(false);
@ -188,6 +203,8 @@ void DecompilerWidget::decompilationFinished(AnnotatedCode code)
ui->textEdit->setPlainText(code.code); ui->textEdit->setPlainText(code.code);
connectCursorPositionChanged(false); connectCursorPositionChanged(false);
updateCursorPosition(); updateCursorPosition();
highlightPC();
highlightBreakpoints();
} }
if (decompilerWasBusy) { if (decompilerWasBusy) {
@ -284,6 +301,8 @@ void DecompilerWidget::updateSelection()
extraSelections.append(createSameWordsSelections(ui->textEdit, searchString)); extraSelections.append(createSameWordsSelections(ui->textEdit, searchString));
ui->textEdit->setExtraSelections(extraSelections); ui->textEdit->setExtraSelections(extraSelections);
// Highlight PC after updating the selected line
highlightPC();
mCtxMenu->setCurHighlightedWord(searchString); mCtxMenu->setCurHighlightedWord(searchString);
} }
@ -327,3 +346,46 @@ bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event)
return MemoryDockWidget::eventFilter(obj, event); return MemoryDockWidget::eventFilter(obj, event);
} }
void DecompilerWidget::highlightPC()
{
RVA PCAddress = Core()->getProgramCounterValue();
if (PCAddress == RVA_INVALID || (Core()->getFunctionStart(PCAddress) != decompiledFunctionAddr)) {
return;
}
QTextCursor cursor = getCursorForAddress(PCAddress);
if (!cursor.isNull()) {
colorLine(createLineHighlightPC(cursor));
}
}
void DecompilerWidget::highlightBreakpoints()
{
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
QTextCursor cursor;
foreach(auto &bp, functionBreakpoints) {
if (bp == RVA_INVALID) {
continue;;
}
cursor = getCursorForAddress(bp);
if (!cursor.isNull()) {
// Use a Block formatting since these lines are not updated frequently as selections and PC
QTextBlockFormat f;
f.setBackground(ConfigColor("gui.breakpoint_background"));
cursor.setBlockFormat(f);
}
}
}
bool DecompilerWidget::colorLine(QTextEdit::ExtraSelection extraSelection)
{
QList<QTextEdit::ExtraSelection> extraSelections = ui->textEdit->extraSelections();
extraSelections.append(extraSelection);
ui->textEdit->setExtraSelections(extraSelections);
return true;
}

View File

@ -1,6 +1,7 @@
#ifndef DECOMPILERWIDGET_H #ifndef DECOMPILERWIDGET_H
#define DECOMPILERWIDGET_H #define DECOMPILERWIDGET_H
#include <QTextEdit>
#include <memory> #include <memory>
#include "core/Cutter.h" #include "core/Cutter.h"
@ -29,6 +30,7 @@ public:
public slots: public slots:
void showDisasContextMenu(const QPoint &pt); void showDisasContextMenu(const QPoint &pt);
void highlightPC();
private slots: private slots:
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void colorsUpdatedSlot(); void colorsUpdatedSlot();
@ -77,6 +79,26 @@ private:
* referenced from the address under cursor * referenced from the address under cursor
*/ */
void seekToReference(); void seekToReference();
/**
* @brief Retrieve the Cursor for a location as close as possible to the given address
* @param addr - an address in the decompiled function
* @return a Cursor object for the given address
*/
QTextCursor getCursorForAddress(RVA addr);
/**
* @brief Append a highlighted line to the TextEdit
* @param extraSelection - an ExtraSelection object colored with the appropriate color
* @return True on success, otherwise False
*/
bool colorLine(QTextEdit::ExtraSelection extraSelection);
/**
* @brief This function responsible to highlight all the breakpoints in the decompiler view.
* It will also run when a breakpoint is added, removed or modified.
*/
void highlightBreakpoints();
}; };
#endif // DECOMPILERWIDGET_H #endif // DECOMPILERWIDGET_H