Heap bins view (#2710)

* Added bins dialog box

* Bins dialog box working

* Add size column

* Add newline

* Make code more readable

* Make headers better

* Add tooltip for bins widget

* Added easy access to detailed chunk info from bins dialog

* Experimenting with CutterGraphView

* Added Basic Graph View for bins using `simpleTextgraphView`

* Added Bins button

* Bug fix

* Add bin message to the last chunk in the list

* Add addresses and addressablecontextmenu to graphs

* Add support multi line graph blocks

* Fix indent

* Dont clear dialog box

* Add `detailed chunk info` in Graphs context menu

* Minor changes

* Update Rizin
This commit is contained in:
Pulak Malhotra 2021-07-02 15:49:27 +05:30 committed by GitHub
parent 2d778c01d7
commit 2d7b6d15c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 682 additions and 10 deletions

2
rizin

@ -1 +1 @@
Subproject commit 92ad8bef001843df5882e71c23761987cfa3d9f4 Subproject commit 381f22d7cc81bf2eb6663a690a715ff4f8f09373

View File

@ -144,6 +144,8 @@ set(SOURCES
dialogs/GlibcHeapInfoDialog.cpp dialogs/GlibcHeapInfoDialog.cpp
widgets/HeapDockWidget.cpp widgets/HeapDockWidget.cpp
widgets/GlibcHeapWidget.cpp widgets/GlibcHeapWidget.cpp
dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp
) )
set(HEADER_FILES set(HEADER_FILES
core/Cutter.h core/Cutter.h
@ -300,6 +302,8 @@ set(HEADER_FILES
dialogs/GlibcHeapInfoDialog.h dialogs/GlibcHeapInfoDialog.h
widgets/HeapDockWidget.h widgets/HeapDockWidget.h
widgets/GlibcHeapWidget.h widgets/GlibcHeapWidget.h
dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h
) )
set(UI_FILES set(UI_FILES
dialogs/AboutDialog.ui dialogs/AboutDialog.ui
@ -369,6 +373,7 @@ set(UI_FILES
dialogs/GlibcHeapInfoDialog.ui dialogs/GlibcHeapInfoDialog.ui
widgets/HeapDockWidget.ui widgets/HeapDockWidget.ui
widgets/GlibcHeapWidget.ui widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui
) )
set(QRC_FILES set(QRC_FILES
resources.qrc resources.qrc

View File

@ -1619,6 +1619,44 @@ RzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr)
return rz_heap_chunk(core, addr); return rz_heap_chunk(core, addr);
} }
QVector<RzHeapBin *> CutterCore::getHeapBins(ut64 arena_addr)
{
CORE_LOCK();
QVector<RzHeapBin *> bins_vector;
MallocState *arena = rz_heap_get_arena(core, arena_addr);
if (!arena) {
return bins_vector;
}
// get small, large, unsorted bins
for (int i = 0; i <= NBINS - 2; i++) {
RzHeapBin *bin = rz_heap_bin_content(core, arena, i, arena_addr);
if (!bin) {
continue;
}
if (!rz_list_length(bin->chunks)) {
rz_heap_bin_free_64(bin);
continue;
}
bins_vector.append(bin);
}
// get fastbins
for (int i = 0; i < 10; i++) {
RzHeapBin *bin = rz_heap_fastbin_content(core, arena, i);
if (!bin) {
continue;
}
if (!rz_list_length(bin->chunks)) {
rz_heap_bin_free_64(bin);
continue;
}
bins_vector.append(bin);
}
return bins_vector;
}
QJsonDocument CutterCore::getChildProcesses(int pid) QJsonDocument CutterCore::getChildProcesses(int pid)
{ {
// Return the currently debugged process and it's children // Return the currently debugged process and it's children

View File

@ -419,6 +419,7 @@ public:
* @return RzHeapChunkSimple struct pointer for the heap chunk * @return RzHeapChunkSimple struct pointer for the heap chunk
*/ */
RzHeapChunkSimple *getHeapChunk(ut64 addr); RzHeapChunkSimple *getHeapChunk(ut64 addr);
QVector<RzHeapBin *> getHeapBins(ut64 arena_addr);
void startDebug(); void startDebug();
void startEmulation(); void startEmulation();
/** /**

View File

@ -0,0 +1,210 @@
#include <HeapBinsGraphView.h>
#include "GlibcHeapBinsDialog.h"
#include "ui_GlibcHeapBinsDialog.h"
#include "GlibcHeapInfoDialog.h"
GlibcHeapBinsDialog::GlibcHeapBinsDialog(RVA m_state, MainWindow *main, QWidget *parent)
: QDialog(parent),
ui(new Ui::GlibcHeapBinsDialog),
m_state(m_state),
binsModel(new BinsModel(m_state, this)),
main(main)
{
ui->setupUi(this);
ui->viewBins->setModel(binsModel);
ui->viewBins->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
ui->viewBins->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
ui->viewBins->verticalHeader()->hide();
ui->viewBins->resizeColumnsToContents();
connect(ui->viewBins->selectionModel(), &QItemSelectionModel::currentChanged, this,
&GlibcHeapBinsDialog::onCurrentChanged);
connect(ui->lineEdit, &QLineEdit::returnPressed, this,
&GlibcHeapBinsDialog::showHeapInfoDialog);
binsModel->reload();
ui->viewBins->resizeColumnsToContents();
graphView = nullptr;
this->setWindowTitle(tr("Bins info for arena @ ") + RAddressString(m_state));
}
GlibcHeapBinsDialog::~GlibcHeapBinsDialog()
{
delete ui;
}
void GlibcHeapBinsDialog::onCurrentChanged(const QModelIndex &current, const QModelIndex &prev)
{
Q_UNUSED(current);
Q_UNUSED(prev);
auto currentIndex = ui->viewBins->selectionModel()->currentIndex();
setChainInfo(currentIndex.row());
setGraphView(currentIndex.row());
}
void GlibcHeapBinsDialog::setChainInfo(int index)
{
// get chunks for the selected bin and construct chain info string
RzListIter *iter;
RzHeapChunkListItem *item;
RzList *chunks = binsModel->getChunks(index);
QString chainInfo;
CutterRListForeach(chunks, iter, RzHeapChunkListItem, item)
{
chainInfo += "" + RAddressString(item->addr);
}
// Add bin message at the end of the list
// responsible for messages like corrupted list, double free
QString message = binsModel->getBinMessage(index);
if (!message.isEmpty()) {
chainInfo += " " + message;
}
ui->chainInfoEdit->setPlainText(chainInfo);
}
void GlibcHeapBinsDialog::showHeapInfoDialog()
{
QString str = ui->lineEdit->text();
if (!str.isEmpty()) {
// summon glibcHeapInfoDialog box with the offset entered
RVA offset = Core()->math(str);
if (!offset) {
ui->lineEdit->setText(QString());
return;
}
GlibcHeapInfoDialog dialog(offset, QString(), this);
dialog.exec();
}
}
void GlibcHeapBinsDialog::setGraphView(int index)
{
if (graphView) {
ui->horizontalLayout->removeWidget(graphView);
delete graphView;
}
graphView = new HeapBinsGraphView(this, binsModel->values[index], main);
ui->horizontalLayout->addWidget(graphView);
graphView->refreshView();
}
BinsModel::BinsModel(RVA arena_addr, QObject *parent)
: QAbstractTableModel(parent), arena_addr(arena_addr)
{
}
void BinsModel::reload()
{
beginResetModel();
clearData();
values = Core()->getHeapBins(arena_addr);
endResetModel();
}
int BinsModel::rowCount(const QModelIndex &) const
{
return values.size();
}
int BinsModel::columnCount(const QModelIndex &) const
{
return ColumnCount;
}
QVariant BinsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= values.count())
return QVariant();
const auto &item = values.at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case BinNumColumn:
return item->bin_num;
case FdColumn:
return (item->fd == 0) ? tr("N/A") : RAddressString(item->fd);
case BkColumn:
return (item->bk == 0) ? tr("N/A") : RAddressString(item->bk);
case TypeColumn:
return tr(item->type);
case CountColumn:
return rz_list_length(item->chunks);
case SizeColumn:
return (item->size == 0) ? tr("N/A") : RHexString(item->size);
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant BinsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation);
switch (role) {
case Qt::DisplayRole:
switch (section) {
case BinNumColumn:
return tr("#");
case FdColumn:
return tr("Fd");
case BkColumn:
return tr("Bk");
case TypeColumn:
return tr("Type");
case CountColumn:
return tr("Chunks count");
case SizeColumn:
return tr("Chunks size");
default:
return QVariant();
}
case Qt::ToolTipRole:
switch (section) {
case BinNumColumn:
return tr("Bin number in NBINS or fastbinsY array");
case FdColumn:
return tr("Pointer to first chunk of the bin");
case BkColumn:
return tr("Pointer to last chunk of the bin");
case TypeColumn:
return tr("Type of bin");
case CountColumn:
return tr("Number of chunks in the bin");
case SizeColumn:
return tr("Size of all chunks in the bin");
default:
return QVariant();
}
default:
return QVariant();
}
}
void BinsModel::clearData()
{
for (auto item : values) {
rz_heap_bin_free_64(item);
}
}
RzList *BinsModel::getChunks(int index)
{
return values[index]->chunks;
}
QString BinsModel::getBinMessage(int index)
{
if (values[index]->message) {
return QString(values[index]->message);
} else {
return QString();
}
}

View File

@ -0,0 +1,63 @@
#ifndef GLIBCHEAPBINSDIALOG_H
#define GLIBCHEAPBINSDIALOG_H
#include <QDialog>
#include <QAbstractTableModel>
#include "core/Cutter.h"
#include <MainWindow.h>
#include <HeapBinsGraphView.h>
namespace Ui {
class GlibcHeapBinsDialog;
}
class BinsModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit BinsModel(RVA arena_addr, QObject *parent = nullptr);
enum Column {
TypeColumn = 0,
BinNumColumn,
FdColumn,
BkColumn,
CountColumn,
SizeColumn,
ColumnCount
};
void reload();
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
void clearData();
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
RVA arena_addr = 0;
RzList *getChunks(int index);
QString getBinMessage(int index);
QVector<RzHeapBin *> values;
private:
};
class GlibcHeapBinsDialog : public QDialog
{
Q_OBJECT
public:
explicit GlibcHeapBinsDialog(RVA i, MainWindow *main, QWidget *parent);
~GlibcHeapBinsDialog();
void onCurrentChanged(const QModelIndex &current, const QModelIndex &prev);
void setChainInfo(int index);
void setGraphView(int index);
private slots:
void showHeapInfoDialog();
private:
Ui::GlibcHeapBinsDialog *ui;
RVA m_state;
BinsModel *binsModel {};
HeapBinsGraphView *graphView;
MainWindow *main;
};
#endif // GLIBCHEAPBINSDIALOG_H

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GlibcHeapBinsDialog</class>
<widget class="QDialog" name="GlibcHeapBinsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>883</width>
<height>544</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableView" name="viewBins"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Chain info:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="chainInfoEdit"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Detailed chunk info:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Enter chunk base address and press enter</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -13,8 +13,13 @@ GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *pa
this->ui->rbNMA->setEnabled(false); this->ui->rbNMA->setEnabled(false);
this->ui->rbPI->setEnabled(false); this->ui->rbPI->setEnabled(false);
this->setWindowTitle(QString("Chunk @ ") + RAddressString(offset) // set window title
+ QString("(" + this->status + ")")); QString windowTitle = tr("Chunk @ ") + RAddressString(offset);
if (!this->status.isEmpty()) {
windowTitle += QString("(" + this->status + ")");
}
this->setWindowTitle(windowTitle);
updateFields(); updateFields();
} }

View File

@ -1,3 +1,4 @@
#include <dialogs/GlibcHeapBinsDialog.h>
#include "GlibcHeapWidget.h" #include "GlibcHeapWidget.h"
#include "ui_GlibcHeapWidget.h" #include "ui_GlibcHeapWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
@ -11,6 +12,8 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)
main(main) main(main)
{ {
ui->setupUi(this); ui->setupUi(this);
viewHeap = ui->tableView;
arenaSelectorView = ui->arenaSelector;
viewHeap->setFont(Config()->getFont()); viewHeap->setFont(Config()->getFont());
viewHeap->setModel(modelHeap); viewHeap->setModel(modelHeap);
@ -18,11 +21,11 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)
// change the scroll mode to ScrollPerPixel // change the scroll mode to ScrollPerPixel
viewHeap->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); viewHeap->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
viewHeap->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); viewHeap->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
ui->verticalLayout->addWidget(viewHeap);
ui->verticalLayout->addWidget(arenaSelectorView);
chunkInfoAction = new QAction(tr("Detailed Chunk Info"), this);
viewHeap->setContextMenuPolicy(Qt::CustomContextMenu); viewHeap->setContextMenuPolicy(Qt::CustomContextMenu);
chunkInfoAction = new QAction(tr("Detailed Chunk Info"), this);
binInfoAction = new QAction(tr("Bins Info"), this);
connect(Core(), &CutterCore::refreshAll, this, &GlibcHeapWidget::updateContents); connect(Core(), &CutterCore::refreshAll, this, &GlibcHeapWidget::updateContents);
connect(Core(), &CutterCore::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents); connect(Core(), &CutterCore::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents);
connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked); connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked);
@ -33,8 +36,11 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)
connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this, connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this,
&GlibcHeapWidget::onCurrentChanged); &GlibcHeapWidget::onCurrentChanged);
connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo); connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo);
connect(binInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewBinInfo);
connect(ui->binsButton, &QPushButton::clicked, this, &GlibcHeapWidget::viewBinInfo);
addressableItemContextMenu.addAction(chunkInfoAction); addressableItemContextMenu.addAction(chunkInfoAction);
addressableItemContextMenu.addAction(binInfoAction);
addActions(addressableItemContextMenu.actions()); addActions(addressableItemContextMenu.actions());
refreshDeferrer = dynamic_cast<CutterDockWidget *>(parent)->createRefreshDeferrer( refreshDeferrer = dynamic_cast<CutterDockWidget *>(parent)->createRefreshDeferrer(
@ -202,3 +208,9 @@ void GlibcHeapWidget::viewChunkInfo()
GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this); GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this);
heapInfoDialog.exec(); heapInfoDialog.exec();
} }
void GlibcHeapWidget::viewBinInfo()
{
GlibcHeapBinsDialog heapBinsDialog(modelHeap->arena_addr, main, this);
heapBinsDialog.exec();
}

View File

@ -43,16 +43,18 @@ private slots:
void customMenuRequested(QPoint pos); void customMenuRequested(QPoint pos);
void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous); void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
void viewChunkInfo(); void viewChunkInfo();
void viewBinInfo();
private: private:
void updateArenas(); void updateArenas();
void updateChunks(); void updateChunks();
Ui::GlibcHeapWidget *ui; Ui::GlibcHeapWidget *ui;
QTableView *viewHeap = new QTableView(this); QTableView *viewHeap;
QComboBox *arenaSelectorView = new QComboBox(this); QComboBox *arenaSelectorView;
GlibcHeapModel *modelHeap = new GlibcHeapModel(this); GlibcHeapModel *modelHeap = new GlibcHeapModel(this);
QVector<Arena> arenas; QVector<Arena> arenas;
QAction *chunkInfoAction; QAction *chunkInfoAction;
QAction *binInfoAction;
AddressableItemContextMenu addressableItemContextMenu; AddressableItemContextMenu addressableItemContextMenu;
RefreshDeferrer *refreshDeferrer {}; RefreshDeferrer *refreshDeferrer {};
MainWindow *main; MainWindow *main;

View File

@ -13,7 +13,34 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"/> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="arenaSelector"/>
</item>
<item>
<widget class="QPushButton" name="binsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>View bins info for an arena</string>
</property>
<property name="text">
<string>Bins</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -0,0 +1,214 @@
#include <Configuration.h>
#include <dialogs/GlibcHeapInfoDialog.h>
#include "HeapBinsGraphView.h"
HeapBinsGraphView::HeapBinsGraphView(QWidget *parent, RzHeapBin *bin, MainWindow *main)
: SimpleTextGraphView(parent, main), heapBin(bin)
{
chunkInfoAction = new QAction(tr("Detailed Chunk Info"), this);
addressableItemContextMenu.addAction(chunkInfoAction);
addAction(chunkInfoAction);
connect(chunkInfoAction, &QAction::triggered, this, &HeapBinsGraphView::viewChunkInfo);
enableAddresses(true);
}
void HeapBinsGraphView::viewChunkInfo()
{
GlibcHeapInfoDialog heapInfoDialog(selectedBlock, QString(), this);
heapInfoDialog.exec();
}
void HeapBinsGraphView::loadCurrentGraph()
{
blockContent.clear();
blocks.clear();
RzListIter *iter;
RzHeapChunkListItem *item;
QVector<GraphHeapChunk> chunks;
// if the bin is a fastbin or not
bool fast = QString(heapBin->type) == QString("Fast");
// store info about the chunks in a vector for easy access
CutterRListForeach(heapBin->chunks, iter, RzHeapChunkListItem, item)
{
GraphHeapChunk graphHeapChunk;
graphHeapChunk.addr = item->addr;
RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr);
if (!chunkInfo) {
break;
}
QString content = "Chunk @ " + RAddressString(chunkInfo->addr) + "\nSize: "
+ RHexString(chunkInfo->size) + "\nFd: " + RAddressString(chunkInfo->fd);
// fastbins lack bk pointer
if (!fast) {
content += "\nBk: " + RAddressString(chunkInfo->bk);
}
graphHeapChunk.fd = chunkInfo->fd;
graphHeapChunk.bk = chunkInfo->bk;
graphHeapChunk.content = content;
chunks.append(graphHeapChunk);
free(chunkInfo);
}
// fast bins have single linked list and other bins have double linked list
if (fast) {
display_single_linked_list(chunks);
} else {
display_double_linked_list(chunks);
}
cleanupEdges(blocks);
computeGraphPlacement();
}
void HeapBinsGraphView::display_single_linked_list(QVector<GraphHeapChunk> chunks)
{
// add the graph block for the bin
GraphLayout::GraphBlock gbBin;
gbBin.entry = 1;
gbBin.edges.emplace_back(heapBin->fd);
QString content = tr(heapBin->type) + tr("bin ") + QString::number(heapBin->bin_num);
content += "\nFd: " + RAddressString(heapBin->fd);
addBlock(gbBin, content);
// add the graph blocks for the chunks
for (int i = 0; i < chunks.size(); i++) {
GraphLayout::GraphBlock gbChunk;
gbChunk.entry = chunks[i].addr;
gbChunk.edges.emplace_back(chunks[i].fd);
if (i == chunks.size() - 1 && heapBin->message) {
chunks[i].content += "\n" + QString(heapBin->message);
}
addBlock(gbChunk, chunks[i].content, chunks[i].addr);
}
// add the END block if no message
if (!heapBin->message) {
GraphLayout::GraphBlock gbEnd;
gbEnd.entry = 0;
addBlock(gbEnd, "END", 0);
}
}
void HeapBinsGraphView::display_double_linked_list(QVector<GraphHeapChunk> chunks)
{
// add the graph block for the bin
GraphLayout::GraphBlock gbBin;
gbBin.entry = heapBin->addr;
gbBin.edges.emplace_back(heapBin->fd);
gbBin.edges.emplace_back(heapBin->bk);
QString content = tr(heapBin->type) + tr("bin ") + QString::number(heapBin->bin_num) + tr(" @ ")
+ RAddressString(heapBin->addr);
content += "\nFd: " + RAddressString(heapBin->fd);
content += "\nBk: " + RAddressString(heapBin->bk);
addBlock(gbBin, content, heapBin->addr);
// add the blocks for the chunks
for (int i = 0; i < chunks.size(); i++) {
GraphLayout::GraphBlock gbChunk;
gbChunk.entry = chunks[i].addr;
gbChunk.edges.emplace_back(chunks[i].fd);
gbChunk.edges.emplace_back(chunks[i].bk);
// if last chunk and there is message then show it in the chunk
if (i == chunks.size() - 1 && heapBin->message) {
chunks[i].content += "\n" + QString(heapBin->message);
}
addBlock(gbChunk, chunks[i].content, chunks[i].addr);
}
}
// overriding this function from SimpleTextGraphView to support multiline text in graph block
// most code is shared from that implementation
void HeapBinsGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive)
{
QRectF blockRect(block.x, block.y, block.width, block.height);
p.setPen(Qt::black);
p.setBrush(Qt::gray);
p.setFont(Config()->getFont());
p.drawRect(blockRect);
// Render node
auto &content = blockContent[block.entry];
p.setPen(QColor(0, 0, 0, 0));
p.setBrush(QColor(0, 0, 0, 100));
p.setPen(QPen(graphNodeColor, 1));
bool blockSelected = interactive && (block.entry == selectedBlock);
if (blockSelected) {
p.setBrush(disassemblySelectedBackgroundColor);
} else {
p.setBrush(disassemblyBackgroundColor);
}
// Draw basic block background
p.drawRect(blockRect);
// Stop rendering text when it's too small
auto transform = p.combinedTransform();
QRect screenChar = transform.mapRect(QRect(0, 0, ACharWidth, charHeight));
if (screenChar.width() < Config()->getGraphMinFontSize()) {
return;
}
p.setPen(palette().color(QPalette::WindowText));
// Render node text
// the only change from SimpleTextGraphView implementation
p.drawText(blockRect, Qt::AlignCenter, content.text);
}
// overriding this function to support multiline text in graph blocks
void HeapBinsGraphView::addBlock(GraphLayout::GraphBlock block, const QString &text, RVA address)
{
auto &content = blockContent[block.entry];
content.text = text;
content.address = address;
int height = 1;
double width = 0;
// split text into different lines
auto lines = text.split(QRegExp("[\n]"), QString::SkipEmptyParts);
// width of the block is the maximum width of a line
for (QString &line : lines) {
width = std::max(mFontMetrics->width(line), width);
}
block.width = static_cast<int>(width + padding);
block.height = (height * charHeight) * lines.length() + padding;
GraphView::addBlock(std::move(block));
}
// overriding to support detailed heap info action in context menu
void HeapBinsGraphView::blockContextMenuRequested(GraphView::GraphBlock &block,
QContextMenuEvent *event, QPoint /*pos*/)
{
if (haveAddresses) {
const auto &content = blockContent[block.entry];
selectedBlock = content.address;
addressableItemContextMenu.setTarget(content.address, content.text);
QPoint pos = event->globalPos();
if (event->reason() != QContextMenuEvent::Mouse) {
QPoint blockPosition(block.x + block.width / 2, block.y + block.height / 2);
blockPosition = logicalToViewCoordinates(blockPosition);
if (viewport()->rect().contains(blockPosition)) {
pos = mapToGlobal(blockPosition);
}
}
addressableItemContextMenu.exec(pos);
event->accept();
}
}

View File

@ -0,0 +1,38 @@
#ifndef CUTTER_HEAPBINSGRAPHVIEW_H
#define CUTTER_HEAPBINSGRAPHVIEW_H
#include "SimpleTextGraphView.h"
class HeapBinsGraphView : public SimpleTextGraphView
{
Q_OBJECT
struct GraphHeapChunk
{
QString content;
ut64 addr;
ut64 fd;
ut64 bk;
};
public:
explicit HeapBinsGraphView(QWidget *parent, RzHeapBin *bin, MainWindow *main);
protected:
void loadCurrentGraph() override;
void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;
void addBlock(GraphLayout::GraphBlock block, const QString &text,
RVA address = RVA_INVALID) override;
void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
QPoint) override;
private slots:
void viewChunkInfo();
private:
RzHeapBin *heapBin;
void display_single_linked_list(QVector<GraphHeapChunk>);
void display_double_linked_list(QVector<GraphHeapChunk>);
QAction *chunkInfoAction;
RVA selectedBlock;
};
#endif // CUTTER_HEAPBINSGRAPHVIEW_H

View File

@ -55,7 +55,8 @@ protected:
* Needs to cleanup the old graph and use addBlock() to create new nodes. * Needs to cleanup the old graph and use addBlock() to create new nodes.
*/ */
virtual void loadCurrentGraph() = 0; virtual void loadCurrentGraph() = 0;
void addBlock(GraphLayout::GraphBlock block, const QString &content, RVA address = RVA_INVALID); virtual void addBlock(GraphLayout::GraphBlock block, const QString &content,
RVA address = RVA_INVALID);
/** /**
* @brief Enable or disable address interactions for nodes. * @brief Enable or disable address interactions for nodes.
* If enabled node addresses need to be specified when calling addBlock(). Adds address related * If enabled node addresses need to be specified when calling addBlock(). Adds address related