From 771fa6102c1ee981f52088de3ed696c29d2ba76d Mon Sep 17 00:00:00 2001 From: Pulak Malhotra <56169176+PulakIIIT@users.noreply.github.com> Date: Mon, 28 Jun 2021 01:51:06 +0530 Subject: [PATCH] Initial heap dock widget with glibc support (#2705) --- rizin | 2 +- src/CMakeLists.txt | 9 ++ src/core/Cutter.cpp | 65 +++++++++ src/core/Cutter.h | 23 ++++ src/core/CutterDescriptions.h | 13 ++ src/core/MainWindow.cpp | 32 +++-- src/core/MainWindow.h | 2 + src/dialogs/GlibcHeapInfoDialog.cpp | 53 ++++++++ src/dialogs/GlibcHeapInfoDialog.h | 26 ++++ src/dialogs/GlibcHeapInfoDialog.ui | 196 ++++++++++++++++++++++++++ src/widgets/GlibcHeapWidget.cpp | 204 ++++++++++++++++++++++++++++ src/widgets/GlibcHeapWidget.h | 61 +++++++++ src/widgets/GlibcHeapWidget.ui | 20 +++ src/widgets/HeapDockWidget.cpp | 41 ++++++ src/widgets/HeapDockWidget.h | 28 ++++ src/widgets/HeapDockWidget.ui | 26 ++++ 16 files changed, 789 insertions(+), 12 deletions(-) create mode 100644 src/dialogs/GlibcHeapInfoDialog.cpp create mode 100644 src/dialogs/GlibcHeapInfoDialog.h create mode 100644 src/dialogs/GlibcHeapInfoDialog.ui create mode 100644 src/widgets/GlibcHeapWidget.cpp create mode 100644 src/widgets/GlibcHeapWidget.h create mode 100644 src/widgets/GlibcHeapWidget.ui create mode 100644 src/widgets/HeapDockWidget.cpp create mode 100644 src/widgets/HeapDockWidget.h create mode 100644 src/widgets/HeapDockWidget.ui diff --git a/rizin b/rizin index e60f0677..92ad8bef 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit e60f06775ebffc7e91903d0b21ad8fee1c86c1ac +Subproject commit 92ad8bef001843df5882e71c23761987cfa3d9f4 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87f14ef5..61935cc5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,6 +141,9 @@ set(SOURCES widgets/AddressableDockWidget.cpp dialogs/preferences/AnalOptionsWidget.cpp common/DecompilerHighlighter.cpp + dialogs/GlibcHeapInfoDialog.cpp + widgets/HeapDockWidget.cpp + widgets/GlibcHeapWidget.cpp ) set(HEADER_FILES core/Cutter.h @@ -294,6 +297,9 @@ set(HEADER_FILES widgets/AddressableDockWidget.h dialogs/preferences/AnalOptionsWidget.h common/DecompilerHighlighter.h + dialogs/GlibcHeapInfoDialog.h + widgets/HeapDockWidget.h + widgets/GlibcHeapWidget.h ) set(UI_FILES dialogs/AboutDialog.ui @@ -360,6 +366,9 @@ set(UI_FILES dialogs/LayoutManager.ui widgets/RizinGraphWidget.ui dialogs/preferences/AnalOptionsWidget.ui + dialogs/GlibcHeapInfoDialog.ui + widgets/HeapDockWidget.ui + widgets/GlibcHeapWidget.ui ) set(QRC_FILES resources.qrc diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 618457dd..19cfaab3 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1556,6 +1556,69 @@ QJsonDocument CutterCore::getProcessThreads(int pid) } } +QVector CutterCore::getHeapChunks(RVA arena_addr) +{ + CORE_LOCK(); + QVector chunks_vector; + ut64 m_arena; + + if (!arena_addr) { + // if arena_addr is zero get base address of main arena + RzList *arenas = rz_heap_arenas_list(core); + if (arenas->length == 0) { + rz_list_free(arenas); + return chunks_vector; + } + m_arena = ((RzArenaListItem *)arenas->head->data)->addr; + rz_list_free(arenas); + } else { + m_arena = arena_addr; + } + + // Get chunks using api and store them in a chunks_vector + RzList *chunks = rz_heap_chunks_list(core, m_arena); + RzListIter *iter; + RzHeapChunkListItem *data; + CutterRListForeach(chunks, iter, RzHeapChunkListItem, data) + { + Chunk chunk; + chunk.offset = data->addr; + chunk.size = (int)data->size; + chunk.status = QString(data->status); + chunks_vector.append(chunk); + } + + rz_list_free(chunks); + return chunks_vector; +} + +QVector CutterCore::getArenas() +{ + CORE_LOCK(); + QVector arena_vector; + + // get arenas using API and store them in arena_vector + RzList *arenas = rz_heap_arenas_list(core); + RzListIter *iter; + RzArenaListItem *data; + CutterRListForeach(arenas, iter, RzArenaListItem, data) + { + Arena arena; + arena.offset = data->addr; + arena.type = QString(data->type); + arena_vector.append(arena); + } + + rz_list_free(arenas); + return arena_vector; +} + +RzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr) +{ + CORE_LOCK(); + return rz_heap_chunk(core, addr); +} + QJsonDocument CutterCore::getChildProcesses(int pid) { // Return the currently debugged process and it's children @@ -1803,6 +1866,7 @@ void CutterCore::attachRemote(const QString &uri) emit toggleDebugView(); } + currentlyRemoteDebugging = true; emit codeRebased(); emit attachedRemote(true); emit debugTaskStateChanged(); @@ -1873,6 +1937,7 @@ void CutterCore::stopDebug() currentlyDebugging = false; currentlyTracing = false; + currentlyRemoteDebugging = false; emit debugTaskStateChanged(); if (currentlyEmulating) { diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 42e3666a..4ac0fee2 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -397,6 +397,28 @@ public: */ QJsonDocument getChildProcesses(int pid); QJsonDocument getBacktrace(); + /** + * @brief Get a list of heap chunks + * Uses RZ_API rz_heap_chunks_list to get vector of chunks + * If arena_addr is zero return the chunks for main arena + * @param arena_addr base address for the arena + * @return Vector of heap chunks for the given arena + */ + QVector getHeapChunks(RVA arena_addr); + + /** + * @brief Get a list of heap arenas + * Uses RZ_API rz_heap_arenas_list to get list of arenas + * @return Vector of arenas + */ + QVector getArenas(); + + /** + * @brief Get detailed information about a heap chunk + * Uses RZ_API rz_heap_chunk + * @return RzHeapChunkSimple struct pointer for the heap chunk + */ + RzHeapChunkSimple *getHeapChunk(ut64 addr); void startDebug(); void startEmulation(); /** @@ -456,6 +478,7 @@ public: bool currentlyDebugging = false; bool currentlyEmulating = false; bool currentlyTracing = false; + bool currentlyRemoteDebugging = false; int currentlyAttachedToPID = -1; QString currentlyOpenFile; diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 166a7d85..2466d2d9 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -364,6 +364,19 @@ struct RegisterRefValueDescription QString ref; }; +struct Chunk +{ + RVA offset; + QString status; + int size; +}; + +struct Arena +{ + RVA offset; + QString type; +}; + Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ExportDescription) diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 7f7165ec..78b8dc7c 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -71,6 +71,7 @@ #include "widgets/HexWidget.h" #include "widgets/RizinGraphWidget.h" #include "widgets/CallGraph.h" +#include "widgets/HeapDockWidget.h" // Qt Headers #include @@ -374,12 +375,15 @@ void MainWindow::initDocks() commentsDock = new CommentsWidget(this); stringsDock = new StringsWidget(this); - QList debugDocks = { - stackDock = new StackWidget(this), threadsDock = new ThreadsWidget(this), - processesDock = new ProcessesWidget(this), backtraceDock = new BacktraceWidget(this), - registersDock = new RegistersWidget(this), memoryMapDock = new MemoryMapWidget(this), - breakpointDock = new BreakpointWidget(this), registerRefsDock = new RegisterRefsWidget(this) - }; + QList debugDocks = { stackDock = new StackWidget(this), + threadsDock = new ThreadsWidget(this), + processesDock = new ProcessesWidget(this), + backtraceDock = new BacktraceWidget(this), + registersDock = new RegistersWidget(this), + memoryMapDock = new MemoryMapWidget(this), + breakpointDock = new BreakpointWidget(this), + registerRefsDock = new RegisterRefsWidget(this), + heapDock = new HeapDockWidget(this) }; QList infoDocks = { classesDock = new ClassesWidget(this), @@ -709,7 +713,8 @@ RzProjectErr MainWindow::saveProjectAs(bool *canceled) QFileDialog fileDialog(this); // Append 'rzdb' suffix if it does not exist fileDialog.setDefaultSuffix("rzdb"); - QString file = fileDialog.getSaveFileName(this, tr("Save Project"), projectFile, PROJECT_FILE_FILTER); + QString file = + fileDialog.getSaveFileName(this, tr("Save Project"), projectFile, PROJECT_FILE_FILTER); if (file.isEmpty()) { if (canceled) { *canceled = true; @@ -918,6 +923,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(stackDock, backtraceDock); tabifyDockWidget(backtraceDock, threadsDock); tabifyDockWidget(threadsDock, processesDock); + tabifyDockWidget(processesDock, heapDock); for (auto dock : pluginDocks) { dockOnMainArea(dock); @@ -928,7 +934,7 @@ bool MainWindow::isDebugWidget(QDockWidget *dock) const { return dock == stackDock || dock == registersDock || dock == backtraceDock || dock == threadsDock || dock == memoryMapDock || dock == breakpointDock - || dock == processesDock || dock == registerRefsDock; + || dock == processesDock || dock == registerRefsDock || dock == heapDock; } bool MainWindow::isExtraMemoryWidget(QDockWidget *dock) const @@ -1264,9 +1270,13 @@ void MainWindow::showZenDocks() void MainWindow::showDebugDocks() { - const QList debugDocks = { functionsDock, stringsDock, searchDock, - stackDock, registersDock, backtraceDock, - threadsDock, memoryMapDock, breakpointDock }; + QList debugDocks = { + functionsDock, stringsDock, searchDock, stackDock, registersDock, + backtraceDock, threadsDock, memoryMapDock, breakpointDock, + }; + if (QSysInfo::kernelType() == "linux" || Core()->currentlyRemoteDebugging) { + debugDocks.append(heapDock); + } functionDockWidthToRestore = functionsDock->maximumWidth(); functionsDock->setMaximumWidth(200); auto registerWidth = qhelpers::forceWidth(registersDock, std::min(500, this->width() / 4)); diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index bd73c1df..312350cd 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -54,6 +54,7 @@ class DecompilerWidget; class OverviewWidget; class RizinGraphWidget; class CallGraphWidget; +class HeapWidget; namespace Ui { class MainWindow; @@ -259,6 +260,7 @@ private: RizinGraphWidget *rzGraphDock = nullptr; CallGraphWidget *callGraphDock = nullptr; CallGraphWidget *globalCallGraphDock = nullptr; + CutterDockWidget *heapDock = nullptr; QMenu *disassemblyContextMenuExtensions = nullptr; QMenu *addressableContextMenuExtensions = nullptr; diff --git a/src/dialogs/GlibcHeapInfoDialog.cpp b/src/dialogs/GlibcHeapInfoDialog.cpp new file mode 100644 index 00000000..3606ee63 --- /dev/null +++ b/src/dialogs/GlibcHeapInfoDialog.cpp @@ -0,0 +1,53 @@ +#include "GlibcHeapInfoDialog.h" + +#include +#include "ui_GlibcHeapInfoDialog.h" + +GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent) + : QDialog(parent), ui(new Ui::GlibcHeapInfoDialog), offset(offset), status(std::move(status)) +{ + ui->setupUi(this); + + // disable all the radio buttons for flag field so they are not user editable + this->ui->rbIM->setEnabled(false); + this->ui->rbNMA->setEnabled(false); + this->ui->rbPI->setEnabled(false); + + this->setWindowTitle(QString("Chunk @ ") + RAddressString(offset) + + QString("(" + this->status + ")")); + updateFields(); +} + +GlibcHeapInfoDialog::~GlibcHeapInfoDialog() +{ + delete ui; +} + +void GlibcHeapInfoDialog::updateFields() +{ + // get data about the heap chunk from the API + RzHeapChunkSimple *chunk = Core()->getHeapChunk(offset); + if (!chunk) { + return; + } + + // Update the information in the widget + this->ui->baseEdit->setText(RAddressString(offset)); + this->ui->sizeEdit->setText(RHexString(chunk->size)); + this->ui->bkEdit->setText(RAddressString(chunk->bk)); + this->ui->fdEdit->setText(RAddressString(chunk->fd)); + this->ui->bknsEdit->setText(RAddressString(chunk->bk_nextsize)); + this->ui->fdnsEdit->setText(RAddressString(chunk->fd_nextsize)); + this->ui->prevSizeEdit->setText(RHexString(chunk->prev_size)); + if (chunk->is_mmapped) { + this->ui->rbIM->setChecked(true); + } + if (chunk->prev_inuse) { + this->ui->rbPI->setChecked(true); + } + if (chunk->non_main_arena) { + this->ui->rbNMA->setChecked(true); + } + + free(chunk); +} diff --git a/src/dialogs/GlibcHeapInfoDialog.h b/src/dialogs/GlibcHeapInfoDialog.h new file mode 100644 index 00000000..88cbb4c4 --- /dev/null +++ b/src/dialogs/GlibcHeapInfoDialog.h @@ -0,0 +1,26 @@ +#ifndef HEAPINFODIALOG_H +#define HEAPINFODIALOG_H + +#include +#include "core/Cutter.h" + +namespace Ui { +class GlibcHeapInfoDialog; +} + +class GlibcHeapInfoDialog : public QDialog +{ + Q_OBJECT + +public: + explicit GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent = nullptr); + ~GlibcHeapInfoDialog(); + +private: + Ui::GlibcHeapInfoDialog *ui; + void updateFields(); + RVA offset; + QString status; +}; + +#endif // HEAPINFODIALOG_H diff --git a/src/dialogs/GlibcHeapInfoDialog.ui b/src/dialogs/GlibcHeapInfoDialog.ui new file mode 100644 index 00000000..48f19c90 --- /dev/null +++ b/src/dialogs/GlibcHeapInfoDialog.ui @@ -0,0 +1,196 @@ + + + GlibcHeapInfoDialog + + + + 0 + 0 + 453 + 263 + + + + Dialog + + + + + + + + Base + + + + + + + Base address of the chunk + + + true + + + + + + + Size + + + + + + + Size of the heap chunk including metadata + + + true + + + + + + + Fd + + + + + + + Link to next free chunk in bin's linked list + + + true + + + + + + + Bk + + + + + + + Link to previous free chunk in bin's linked list + + + true + + + + + + + Link to next larger free chunk (only for large chunks) + + + true + + + + + + + <html><head/><body><p>Fd-nextsize</p></body></html> + + + + + + + Bk-nextsize + + + + + + + Link to next smaller free chunk (for large chunks) + + + true + + + + + + + Size of previous chunk (if free) + + + true + + + + + + + PrevSize + + + + + + + + + + + If the chunk was obtained from a non-main arena + + + NON_MAIN_ARENA + + + true + + + false + + + + + + + The chunk was obtained with mmap() + + + IS_MMAPED + + + true + + + false + + + + + + + Previous adjacent chunk is in use + + + PREV_INUSE + + + true + + + false + + + + + + + + + + diff --git a/src/widgets/GlibcHeapWidget.cpp b/src/widgets/GlibcHeapWidget.cpp new file mode 100644 index 00000000..0c466b69 --- /dev/null +++ b/src/widgets/GlibcHeapWidget.cpp @@ -0,0 +1,204 @@ +#include "GlibcHeapWidget.h" +#include "ui_GlibcHeapWidget.h" +#include "core/MainWindow.h" +#include "QHeaderView" +#include "dialogs/GlibcHeapInfoDialog.h" + +GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent) + : QWidget(parent), + ui(new Ui::GlibcHeapWidget), + addressableItemContextMenu(this, main), + main(main) +{ + ui->setupUi(this); + + viewHeap->setFont(Config()->getFont()); + viewHeap->setModel(modelHeap); + viewHeap->verticalHeader()->hide(); + // 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); + + connect(Core(), &CutterCore::refreshAll, this, &GlibcHeapWidget::updateContents); + connect(Core(), &CutterCore::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents); + connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked); + connect(arenaSelectorView, &QComboBox::currentIndexChanged, this, + &GlibcHeapWidget::onArenaSelected); + connect(viewHeap, &QWidget::customContextMenuRequested, this, + &GlibcHeapWidget::customMenuRequested); + connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this, + &GlibcHeapWidget::onCurrentChanged); + connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo); + + addressableItemContextMenu.addAction(chunkInfoAction); + addActions(addressableItemContextMenu.actions()); + + refreshDeferrer = dynamic_cast(parent)->createRefreshDeferrer( + [this]() { updateContents(); }); +} + +GlibcHeapWidget::~GlibcHeapWidget() +{ + delete ui; +} + +GlibcHeapModel::GlibcHeapModel(QObject *parent) : QAbstractTableModel(parent) {} + +void GlibcHeapWidget::updateArenas() +{ + arenas = Core()->getArenas(); + + // store the currently selected arena's index + int currentIndex = arenaSelectorView->currentIndex(); + arenaSelectorView->clear(); + + // add the new arenas to the arena selector + for (auto &arena : arenas) { + arenaSelectorView->addItem(RAddressString(arena.offset) + + QString(" (" + arena.type + " Arena)")); + } + + // check if arenas reduced or invalid index and restore the previously selected arena + if (arenaSelectorView->count() < currentIndex || currentIndex == -1) { + currentIndex = 0; + } + arenaSelectorView->setCurrentIndex(currentIndex); +} + +void GlibcHeapWidget::onArenaSelected(int index) +{ + if (index == -1) { + modelHeap->arena_addr = 0; + } else { + modelHeap->arena_addr = arenas[index].offset; + } + + updateChunks(); +} + +void GlibcHeapWidget::updateContents() +{ + if (!refreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) { + return; + } + + updateArenas(); + updateChunks(); +} + +void GlibcHeapWidget::updateChunks() +{ + modelHeap->reload(); + viewHeap->resizeColumnsToContents(); +} + +void GlibcHeapWidget::customMenuRequested(QPoint pos) +{ + addressableItemContextMenu.exec(viewHeap->viewport()->mapToGlobal(pos)); +} + +void GlibcHeapModel::reload() +{ + beginResetModel(); + values.clear(); + values = Core()->getHeapChunks(arena_addr); + endResetModel(); +} + +int GlibcHeapModel::columnCount(const QModelIndex &) const +{ + return ColumnCount; +} + +int GlibcHeapModel::rowCount(const QModelIndex &) const +{ + return this->values.size(); +} + +QVariant GlibcHeapModel::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 OffsetColumn: + return RAddressString(item.offset); + case SizeColumn: + return RHexString(item.size); + case StatusColumn: + return item.status; + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +QVariant GlibcHeapModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(orientation); + switch (role) { + case Qt::DisplayRole: + switch (section) { + case OffsetColumn: + return tr("Offset"); + case SizeColumn: + return tr("Size"); + case StatusColumn: + return tr("Status"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void GlibcHeapWidget::onDoubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) { + return; + } + + int column = index.column(); + if (column == GlibcHeapModel::OffsetColumn) { + QString item = index.data().toString(); + Core()->seek(item); + main->showMemoryWidget(MemoryWidgetType::Hexdump); + } +} + +void GlibcHeapWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &prev) +{ + Q_UNUSED(current) + Q_UNUSED(prev) + + auto currentIndex = viewHeap->selectionModel()->currentIndex(); + QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn) + .data() + .toString(); + addressableItemContextMenu.setTarget(Core()->math(offsetString)); +} + +void GlibcHeapWidget::viewChunkInfo() +{ + auto currentIndex = viewHeap->selectionModel()->currentIndex(); + QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn) + .data() + .toString(); + QString status = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::StatusColumn) + .data() + .toString(); + + GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this); + heapInfoDialog.exec(); +} diff --git a/src/widgets/GlibcHeapWidget.h b/src/widgets/GlibcHeapWidget.h new file mode 100644 index 00000000..459e247f --- /dev/null +++ b/src/widgets/GlibcHeapWidget.h @@ -0,0 +1,61 @@ +#ifndef GLIBCHEAPWIDGET_H +#define GLIBCHEAPWIDGET_H + +#include +#include "CutterDockWidget.h" +#include "core/Cutter.h" +#include +#include +#include + +namespace Ui { +class GlibcHeapWidget; +} + +class GlibcHeapModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit GlibcHeapModel(QObject *parent = nullptr); + enum Column { OffsetColumn = 0, SizeColumn, StatusColumn, ColumnCount }; + void reload(); + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + RVA arena_addr = 0; + +private: + QVector values; +}; + +class GlibcHeapWidget : public QWidget +{ + Q_OBJECT + +public: + explicit GlibcHeapWidget(MainWindow *main, QWidget *parent); + ~GlibcHeapWidget(); +private slots: + void updateContents(); + void onDoubleClicked(const QModelIndex &index); + void onArenaSelected(int index); + void customMenuRequested(QPoint pos); + void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void viewChunkInfo(); + +private: + void updateArenas(); + void updateChunks(); + Ui::GlibcHeapWidget *ui; + QTableView *viewHeap = new QTableView(this); + QComboBox *arenaSelectorView = new QComboBox(this); + GlibcHeapModel *modelHeap = new GlibcHeapModel(this); + QVector arenas; + QAction *chunkInfoAction; + AddressableItemContextMenu addressableItemContextMenu; + RefreshDeferrer *refreshDeferrer {}; + MainWindow *main; +}; + +#endif // GLIBCHEAPWIDGET_H diff --git a/src/widgets/GlibcHeapWidget.ui b/src/widgets/GlibcHeapWidget.ui new file mode 100644 index 00000000..db9b6184 --- /dev/null +++ b/src/widgets/GlibcHeapWidget.ui @@ -0,0 +1,20 @@ + + + GlibcHeapWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + diff --git a/src/widgets/HeapDockWidget.cpp b/src/widgets/HeapDockWidget.cpp new file mode 100644 index 00000000..428fb52a --- /dev/null +++ b/src/widgets/HeapDockWidget.cpp @@ -0,0 +1,41 @@ +#include "HeapDockWidget.h" +#include "ui_HeapDockWidget.h" +#include "widgets/GlibcHeapWidget.h" + +HeapDockWidget::HeapDockWidget(MainWindow *main) + : CutterDockWidget(main), ui(new Ui::HeapDockWidget), main(main) +{ + ui->setupUi(this); + + ui->allocatorSelector->addItem("Glibc Heap"); + ui->verticalLayout->setMargin(0); + + connect(ui->allocatorSelector, &QComboBox::currentIndexChanged, this, + &HeapDockWidget::onAllocatorSelected); + + // select Glibc heap by default + onAllocatorSelected(0); +} + +HeapDockWidget::~HeapDockWidget() +{ + delete ui; +} + +void HeapDockWidget::onAllocatorSelected(int index) +{ + if (index >= AllocatorCount) + return; + + // remove the current heap widget from layout + if (currentHeapWidget) { + ui->verticalLayout->removeWidget(currentHeapWidget); + delete currentHeapWidget; + } + + // change widget depending upon selected allocator + if (index == Glibc) { + currentHeapWidget = new GlibcHeapWidget(main, this); + } + ui->verticalLayout->addWidget(currentHeapWidget); +} diff --git a/src/widgets/HeapDockWidget.h b/src/widgets/HeapDockWidget.h new file mode 100644 index 00000000..4e507c39 --- /dev/null +++ b/src/widgets/HeapDockWidget.h @@ -0,0 +1,28 @@ +#ifndef HEAPDOCKWIDGET_H +#define HEAPDOCKWIDGET_H + +#include +#include "CutterDockWidget.h" + +namespace Ui { +class HeapDockWidget; +} + +class HeapDockWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit HeapDockWidget(MainWindow *main); + ~HeapDockWidget(); +private slots: + void onAllocatorSelected(int index); + +private: + enum Allocator { Glibc = 0, AllocatorCount }; + Ui::HeapDockWidget *ui; + MainWindow *main; + QWidget* currentHeapWidget = nullptr; +}; + +#endif // HEAPDOCKWIDGET_H diff --git a/src/widgets/HeapDockWidget.ui b/src/widgets/HeapDockWidget.ui new file mode 100644 index 00000000..50cd3906 --- /dev/null +++ b/src/widgets/HeapDockWidget.ui @@ -0,0 +1,26 @@ + + + HeapDockWidget + + + + 0 + 0 + 400 + 300 + + + + Heap + + + + + + + + + + + +