diff --git a/rizin b/rizin index 92ad8bef..381f22d7 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 92ad8bef001843df5882e71c23761987cfa3d9f4 +Subproject commit 381f22d7cc81bf2eb6663a690a715ff4f8f09373 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 61935cc5..147caa56 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -144,6 +144,8 @@ set(SOURCES dialogs/GlibcHeapInfoDialog.cpp widgets/HeapDockWidget.cpp widgets/GlibcHeapWidget.cpp + dialogs/GlibcHeapBinsDialog.cpp + widgets/HeapBinsGraphView.cpp ) set(HEADER_FILES core/Cutter.h @@ -300,6 +302,8 @@ set(HEADER_FILES dialogs/GlibcHeapInfoDialog.h widgets/HeapDockWidget.h widgets/GlibcHeapWidget.h + dialogs/GlibcHeapBinsDialog.h + widgets/HeapBinsGraphView.h ) set(UI_FILES dialogs/AboutDialog.ui @@ -369,6 +373,7 @@ set(UI_FILES dialogs/GlibcHeapInfoDialog.ui widgets/HeapDockWidget.ui widgets/GlibcHeapWidget.ui + dialogs/GlibcHeapBinsDialog.ui ) set(QRC_FILES resources.qrc diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 6894ec15..b5856535 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1619,6 +1619,44 @@ RzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr) return rz_heap_chunk(core, addr); } +QVector CutterCore::getHeapBins(ut64 arena_addr) +{ + CORE_LOCK(); + QVector 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) { // Return the currently debugged process and it's children diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 4ac0fee2..d728d18c 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -419,6 +419,7 @@ public: * @return RzHeapChunkSimple struct pointer for the heap chunk */ RzHeapChunkSimple *getHeapChunk(ut64 addr); + QVector getHeapBins(ut64 arena_addr); void startDebug(); void startEmulation(); /** diff --git a/src/dialogs/GlibcHeapBinsDialog.cpp b/src/dialogs/GlibcHeapBinsDialog.cpp new file mode 100644 index 00000000..8d0aeede --- /dev/null +++ b/src/dialogs/GlibcHeapBinsDialog.cpp @@ -0,0 +1,210 @@ +#include +#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 ¤t, 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(); + } +} diff --git a/src/dialogs/GlibcHeapBinsDialog.h b/src/dialogs/GlibcHeapBinsDialog.h new file mode 100644 index 00000000..a3561161 --- /dev/null +++ b/src/dialogs/GlibcHeapBinsDialog.h @@ -0,0 +1,63 @@ +#ifndef GLIBCHEAPBINSDIALOG_H +#define GLIBCHEAPBINSDIALOG_H + +#include +#include +#include "core/Cutter.h" +#include +#include + +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 values; + +private: +}; + +class GlibcHeapBinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit GlibcHeapBinsDialog(RVA i, MainWindow *main, QWidget *parent); + ~GlibcHeapBinsDialog(); + void onCurrentChanged(const QModelIndex ¤t, 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 diff --git a/src/dialogs/GlibcHeapBinsDialog.ui b/src/dialogs/GlibcHeapBinsDialog.ui new file mode 100644 index 00000000..36c7a689 --- /dev/null +++ b/src/dialogs/GlibcHeapBinsDialog.ui @@ -0,0 +1,56 @@ + + + GlibcHeapBinsDialog + + + + 0 + 0 + 883 + 544 + + + + Dialog + + + + + + + + + + + Chain info: + + + + + + + + + + + + Detailed chunk info: + + + + + + + Enter chunk base address and press enter + + + + + + + + + + + + diff --git a/src/dialogs/GlibcHeapInfoDialog.cpp b/src/dialogs/GlibcHeapInfoDialog.cpp index 3606ee63..9e9f8c80 100644 --- a/src/dialogs/GlibcHeapInfoDialog.cpp +++ b/src/dialogs/GlibcHeapInfoDialog.cpp @@ -13,8 +13,13 @@ GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *pa this->ui->rbNMA->setEnabled(false); this->ui->rbPI->setEnabled(false); - this->setWindowTitle(QString("Chunk @ ") + RAddressString(offset) - + QString("(" + this->status + ")")); + // set window title + QString windowTitle = tr("Chunk @ ") + RAddressString(offset); + if (!this->status.isEmpty()) { + windowTitle += QString("(" + this->status + ")"); + } + this->setWindowTitle(windowTitle); + updateFields(); } diff --git a/src/widgets/GlibcHeapWidget.cpp b/src/widgets/GlibcHeapWidget.cpp index 0c466b69..83378ad2 100644 --- a/src/widgets/GlibcHeapWidget.cpp +++ b/src/widgets/GlibcHeapWidget.cpp @@ -1,3 +1,4 @@ +#include #include "GlibcHeapWidget.h" #include "ui_GlibcHeapWidget.h" #include "core/MainWindow.h" @@ -11,6 +12,8 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent) main(main) { ui->setupUi(this); + viewHeap = ui->tableView; + arenaSelectorView = ui->arenaSelector; viewHeap->setFont(Config()->getFont()); viewHeap->setModel(modelHeap); @@ -18,11 +21,11 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent) // change the scroll mode to ScrollPerPixel viewHeap->setHorizontalScrollMode(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); + 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::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents); connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked); @@ -33,8 +36,11 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent) connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this, &GlibcHeapWidget::onCurrentChanged); 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(binInfoAction); addActions(addressableItemContextMenu.actions()); refreshDeferrer = dynamic_cast(parent)->createRefreshDeferrer( @@ -202,3 +208,9 @@ void GlibcHeapWidget::viewChunkInfo() GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this); heapInfoDialog.exec(); } + +void GlibcHeapWidget::viewBinInfo() +{ + GlibcHeapBinsDialog heapBinsDialog(modelHeap->arena_addr, main, this); + heapBinsDialog.exec(); +} diff --git a/src/widgets/GlibcHeapWidget.h b/src/widgets/GlibcHeapWidget.h index 459e247f..da6f110f 100644 --- a/src/widgets/GlibcHeapWidget.h +++ b/src/widgets/GlibcHeapWidget.h @@ -43,16 +43,18 @@ private slots: void customMenuRequested(QPoint pos); void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); void viewChunkInfo(); + void viewBinInfo(); private: void updateArenas(); void updateChunks(); Ui::GlibcHeapWidget *ui; - QTableView *viewHeap = new QTableView(this); - QComboBox *arenaSelectorView = new QComboBox(this); + QTableView *viewHeap; + QComboBox *arenaSelectorView; GlibcHeapModel *modelHeap = new GlibcHeapModel(this); QVector arenas; QAction *chunkInfoAction; + QAction *binInfoAction; AddressableItemContextMenu addressableItemContextMenu; RefreshDeferrer *refreshDeferrer {}; MainWindow *main; diff --git a/src/widgets/GlibcHeapWidget.ui b/src/widgets/GlibcHeapWidget.ui index db9b6184..ae2bd9f8 100644 --- a/src/widgets/GlibcHeapWidget.ui +++ b/src/widgets/GlibcHeapWidget.ui @@ -13,7 +13,34 @@ Form - + + + + + + + + + + + + + + 0 + 0 + + + + View bins info for an arena + + + Bins + + + + + + diff --git a/src/widgets/HeapBinsGraphView.cpp b/src/widgets/HeapBinsGraphView.cpp new file mode 100644 index 00000000..6d987468 --- /dev/null +++ b/src/widgets/HeapBinsGraphView.cpp @@ -0,0 +1,214 @@ +#include +#include +#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 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 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 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(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(); + } +} \ No newline at end of file diff --git a/src/widgets/HeapBinsGraphView.h b/src/widgets/HeapBinsGraphView.h new file mode 100644 index 00000000..99b8c128 --- /dev/null +++ b/src/widgets/HeapBinsGraphView.h @@ -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); + void display_double_linked_list(QVector); + QAction *chunkInfoAction; + RVA selectedBlock; +}; + +#endif // CUTTER_HEAPBINSGRAPHVIEW_H diff --git a/src/widgets/SimpleTextGraphView.h b/src/widgets/SimpleTextGraphView.h index cc1cf25f..a9a87bf7 100644 --- a/src/widgets/SimpleTextGraphView.h +++ b/src/widgets/SimpleTextGraphView.h @@ -55,7 +55,8 @@ protected: * Needs to cleanup the old graph and use addBlock() to create new nodes. */ 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. * If enabled node addresses need to be specified when calling addBlock(). Adds address related