Skip to content

Commit e63411e

Browse files
Copilotxusheng6
andcommitted
Move copy functionality to threadframes widget and revert stackwidget changes
Co-authored-by: xusheng6 <[email protected]>
1 parent 8424b18 commit e63411e

File tree

4 files changed

+120
-139
lines changed

4 files changed

+120
-139
lines changed

ui/stackwidget.cpp

Lines changed: 3 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ limitations under the License.
1717
#include <QPainter>
1818
#include <QHeaderView>
1919
#include <QLineEdit>
20-
#include <QGuiApplication>
21-
#include <QMimeData>
22-
#include <QClipboard>
2320
#include "stackwidget.h"
2421
#include "fmt/format.h"
2522

@@ -412,18 +409,6 @@ DebugStackWidget::DebugStackWidget(const QString& name, ViewFrame* view, BinaryV
412409
layout->addWidget(m_table);
413410
setLayout(layout);
414411

415-
m_actionHandler.setupActionHandler(this);
416-
m_contextMenuManager = new ContextMenuManager(this);
417-
m_menu = new Menu();
418-
419-
m_menu->addAction("Copy", "Options", MENU_ORDER_NORMAL);
420-
m_actionHandler.bindAction("Copy", UIAction([&]() { copy(); }, [&]() { return selectionNotEmpty(); }));
421-
422-
QString actionName = QString::fromStdString("Copy All Stack Entries");
423-
UIAction::registerAction(actionName);
424-
m_menu->addAction(actionName, "Options", MENU_ORDER_NORMAL);
425-
m_actionHandler.bindAction(actionName, UIAction([this]() { copyAll(); }));
426-
427412
updateContent();
428413
}
429414

@@ -436,99 +421,10 @@ void DebugStackWidget::notifyStackChanged(std::vector<DebugStackItem> stackItems
436421
}
437422

438423

439-
void DebugStackWidget::contextMenuEvent(QContextMenuEvent* event)
440-
{
441-
showContextMenu();
442-
}
443-
444-
445-
void DebugStackWidget::showContextMenu()
446-
{
447-
m_contextMenuManager->show(m_menu, &m_actionHandler);
448-
}
449-
450-
451-
bool DebugStackWidget::selectionNotEmpty()
452-
{
453-
QModelIndexList sel = m_table->selectionModel()->selectedRows();
454-
return !sel.empty();
455-
}
456-
457-
458-
void DebugStackWidget::copy()
459-
{
460-
QModelIndexList sel = m_table->selectionModel()->selectedRows();
461-
if (sel.empty())
462-
return;
463-
464-
QString text;
465-
for (int i = 0; i < sel.size(); i++)
466-
{
467-
if (i > 0)
468-
text += "\n";
469-
470-
int row = sel[i].row();
471-
if (row < 0 || row >= m_model->rowCount())
472-
continue;
473-
474-
DebugStackItem item = m_model->getRow(row);
475-
476-
// Format: Offset Address Value Hint
477-
QString offsetStr;
478-
ptrdiff_t offset = item.offset();
479-
if (offset < 0)
480-
offsetStr = QString::asprintf("-0x%" PRIx64, (uint64_t)-offset);
481-
else
482-
offsetStr = QString::asprintf("0x%" PRIx64, (uint64_t)offset);
483-
484-
text += QString::asprintf("%s 0x%" PRIx64 " 0x%" PRIx64 " %s", offsetStr.toStdString().c_str(), item.address(),
485-
item.value(), item.hint().c_str());
486-
}
487-
488-
auto* clipboard = QGuiApplication::clipboard();
489-
clipboard->clear();
490-
auto* mime = new QMimeData();
491-
mime->setText(text);
492-
clipboard->setMimeData(mime);
493-
}
494-
495-
496-
void DebugStackWidget::copyAll()
497-
{
498-
QString text;
499-
int rowCount = m_model->rowCount();
500-
501-
for (int row = 0; row < rowCount; row++)
502-
{
503-
if (row > 0)
504-
text += "\n";
505-
506-
DebugStackItem item = m_model->getRow(row);
507-
508-
// Format: Offset Address Value Hint
509-
QString offsetStr;
510-
ptrdiff_t offset = item.offset();
511-
if (offset < 0)
512-
offsetStr = QString::asprintf("-0x%" PRIx64, (uint64_t)-offset);
513-
else
514-
offsetStr = QString::asprintf("0x%" PRIx64, (uint64_t)offset);
515-
516-
text += QString::asprintf("%s 0x%" PRIx64 " 0x%" PRIx64 " %s", offsetStr.toStdString().c_str(), item.address(),
517-
item.value(), item.hint().c_str());
518-
}
519-
520-
auto* clipboard = QGuiApplication::clipboard();
521-
clipboard->clear();
522-
auto* mime = new QMimeData();
523-
mime->setText(text);
524-
clipboard->setMimeData(mime);
525-
}
526-
527-
528-
// void DebugStackWidget::notifyFontChanged()
424+
//void DebugStackWidget::notifyFontChanged()
529425
//{
530-
// m_delegate->updateFonts();
531-
// }
426+
// m_delegate->updateFonts();
427+
//}
532428

533429

534430
void DebugStackWidget::updateContent()

ui/stackwidget.h

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,12 @@ limitations under the License.
2121
#include <QModelIndex>
2222
#include <QTableView>
2323
#include <QStyledItemDelegate>
24-
#include <QGuiApplication>
25-
#include <QMimeData>
26-
#include <QClipboard>
2724
#include "inttypes.h"
2825
#include "binaryninjaapi.h"
2926
#include "viewframe.h"
3027
#include "fontsettings.h"
3128
#include "theme.h"
3229
#include "debuggerapi.h"
33-
#include "menus.h"
3430

3531
using namespace BinaryNinjaDebuggerAPI;
3632

@@ -142,27 +138,15 @@ class DebugStackWidget : public QWidget
142138
DebugStackListModel* m_model;
143139
DebugStackItemDelegate* m_delegate;
144140

145-
UIActionHandler m_actionHandler;
146-
ContextMenuManager* m_contextMenuManager;
147-
Menu* m_menu;
141+
//void shouldBeVisible()
148142

149-
virtual void contextMenuEvent(QContextMenuEvent* event) override;
150-
bool selectionNotEmpty();
151-
152-
// void shouldBeVisible()
153-
154-
// virtual void notifyFontChanged() override;
143+
//virtual void notifyFontChanged() override;
155144

156145

157146
public:
158147
DebugStackWidget(const QString& name, ViewFrame* view, BinaryViewRef data);
159148
void notifyStackChanged(std::vector<DebugStackItem> stackItems);
160149

161-
private slots:
162-
void copy();
163-
void copyAll();
164-
165150
public slots:
166151
void updateContent();
167-
void showContextMenu();
168152
};

ui/threadframes.cpp

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ int FrameItem::row() const
5858
}
5959

6060

61-
ThreadFrameModel::ThreadFrameModel(QObject* parent, DebuggerControllerRef controller) : QAbstractItemModel(parent), m_controller(controller)
61+
ThreadFrameModel::ThreadFrameModel(QObject* parent, DebuggerControllerRef controller) :
62+
QAbstractItemModel(parent), m_controller(controller)
6263
{
6364
rootItem = new FrameItem();
6465
}
@@ -109,7 +110,8 @@ QVariant ThreadFrameModel::data(const QModelIndex& index, int role) const
109110

110111
auto isActiveThread = m_controller->GetActiveThread().m_tid == item->tid();
111112

112-
QString text = QString::asprintf("%s0x%x @ 0x%" PRIx64, isActiveThread ? "(*) " : "", item->tid(), item->threadPc());
113+
QString text =
114+
QString::asprintf("%s0x%x @ 0x%" PRIx64, isActiveThread ? "(*) " : "", item->tid(), item->threadPc());
113115
if (role == Qt::SizeHintRole)
114116
return QVariant((qulonglong)text.size());
115117

@@ -182,20 +184,22 @@ void ThreadFrameModel::updateRows(DebuggerController* controller)
182184
parents << rootItem;
183185

184186
std::vector<DebugThread> threads = controller->GetThreads();
185-
187+
186188
// Sort threads so that the active thread appears first
187189
uint32_t activeThreadId = controller->GetActiveThread().m_tid;
188190
std::sort(threads.begin(), threads.end(), [activeThreadId](const DebugThread& a, const DebugThread& b) {
189191
// Active thread comes first, then sort by thread ID for consistent ordering
190192
bool aIsActive = (a.m_tid == activeThreadId);
191193
bool bIsActive = (b.m_tid == activeThreadId);
192-
193-
if (aIsActive && !bIsActive) return true;
194-
if (!aIsActive && bIsActive) return false;
195-
194+
195+
if (aIsActive && !bIsActive)
196+
return true;
197+
if (!aIsActive && bIsActive)
198+
return false;
199+
196200
return a.m_tid < b.m_tid;
197201
});
198-
202+
199203
for (const DebugThread& thread : threads)
200204
{
201205
parents.last()->appendChild(new FrameItem(thread, parents.last()));
@@ -550,6 +554,92 @@ void ThreadFramesWidget::copy()
550554
}
551555

552556

557+
void ThreadFramesWidget::copyCurrentFrame()
558+
{
559+
QModelIndexList sel = selectionModel()->selectedIndexes();
560+
if (sel.empty())
561+
return;
562+
563+
QString text;
564+
QSet<int> processedRows;
565+
566+
for (const QModelIndex& index : sel)
567+
{
568+
if (!index.isValid())
569+
continue;
570+
571+
int row = index.row();
572+
if (processedRows.contains(row))
573+
continue;
574+
575+
processedRows.insert(row);
576+
577+
FrameItem* item = static_cast<FrameItem*>(index.internalPointer());
578+
if (!item || !item->isFrame())
579+
continue;
580+
581+
if (!text.isEmpty())
582+
text += "\n";
583+
584+
// Format: FrameIndex Module Function PC SP FP
585+
text += QString::asprintf("%lu %s %s 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64, item->frameIndex(),
586+
item->module().c_str(), item->function().c_str(), item->framePc(), item->sp(), item->fp());
587+
}
588+
589+
if (text.isEmpty())
590+
return;
591+
592+
auto* clipboard = QGuiApplication::clipboard();
593+
clipboard->clear();
594+
auto* mime = new QMimeData();
595+
mime->setText(text);
596+
clipboard->setMimeData(mime);
597+
}
598+
599+
600+
void ThreadFramesWidget::copyAllFrames()
601+
{
602+
QString text;
603+
604+
// Iterate through all top-level items (threads)
605+
for (int i = 0; i < m_model->rowCount(); i++)
606+
{
607+
QModelIndex threadIndex = m_model->index(i, 0);
608+
if (!threadIndex.isValid())
609+
continue;
610+
611+
FrameItem* threadItem = static_cast<FrameItem*>(threadIndex.internalPointer());
612+
if (!threadItem)
613+
continue;
614+
615+
// Iterate through all frames in this thread
616+
for (int j = 0; j < threadItem->childCount(); j++)
617+
{
618+
FrameItem* frameItem = threadItem->child(j);
619+
if (!frameItem || !frameItem->isFrame())
620+
continue;
621+
622+
if (!text.isEmpty())
623+
text += "\n";
624+
625+
// Format: FrameIndex Module Function PC SP FP
626+
text += QString::asprintf("%lu %s %s 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64, frameItem->frameIndex(),
627+
frameItem->module().c_str(), frameItem->function().c_str(), frameItem->framePc(), frameItem->sp(),
628+
frameItem->fp());
629+
}
630+
}
631+
632+
if (text.isEmpty())
633+
return;
634+
635+
auto* clipboard = QGuiApplication::clipboard();
636+
clipboard->clear();
637+
auto* mime = new QMimeData();
638+
mime->setText(text);
639+
clipboard->setMimeData(mime);
640+
}
641+
642+
553643
ThreadFramesWidget::ThreadFramesWidget(QWidget* parent, ViewFrame* frame, BinaryViewRef data) :
554644
QTreeView(parent), m_view(frame)
555645
{
@@ -585,7 +675,8 @@ ThreadFramesWidget::ThreadFramesWidget(QWidget* parent, ViewFrame* frame, Binary
585675
actionName = QString::fromStdString("Resume Thread");
586676
UIAction::registerAction(actionName);
587677
m_menu->addAction(actionName, "Options", MENU_ORDER_FIRST);
588-
m_actionHandler.bindAction(actionName, UIAction([this]() { resumeThread(); }, [this]() { return canSuspendOrResume(); }));
678+
m_actionHandler.bindAction(
679+
actionName, UIAction([this]() { resumeThread(); }, [this]() { return canSuspendOrResume(); }));
589680

590681
actionName = QString::fromStdString("Make It Solo Thread");
591682
UIAction::registerAction(actionName);
@@ -623,16 +714,24 @@ ThreadFramesWidget::ThreadFramesWidget(QWidget* parent, ViewFrame* frame, Binary
623714
}
624715
});
625716

717+
actionName = QString::fromStdString("Copy Current Stack Trace");
718+
UIAction::registerAction(actionName);
719+
m_menu->addAction(actionName, "Options", MENU_ORDER_NORMAL);
720+
m_actionHandler.bindAction(
721+
actionName, UIAction([this]() { copyCurrentFrame(); }, [this]() { return selectionNotEmpty(); }));
722+
723+
actionName = QString::fromStdString("Copy All Stack Traces");
724+
UIAction::registerAction(actionName);
725+
m_menu->addAction(actionName, "Options", MENU_ORDER_NORMAL);
726+
m_actionHandler.bindAction(actionName, UIAction([this]() { copyAllFrames(); }));
727+
626728
// TODO: set as active thread action?
627729

628730
connect(this, &QTreeView::doubleClicked, this, &ThreadFramesWidget::onDoubleClicked);
629731
connect(this, &ThreadFramesWidget::debuggerEvent, this, &ThreadFramesWidget::onDebuggerEvent);
630732

631733
m_debuggerEventCallback = m_debugger->RegisterEventCallback(
632-
[&](const DebuggerEvent& event) {
633-
emit debuggerEvent(event);
634-
},
635-
"Thread Frame");
734+
[&](const DebuggerEvent& event) { emit debuggerEvent(event); }, "Thread Frame");
636735

637736
updateContent();
638737
}
@@ -721,7 +820,7 @@ void ThreadFramesWidget::onDoubleClicked()
721820
{
722821
uint32_t tid = frameItem->tid();
723822
uint32_t currentTid = m_debugger->GetActiveThread().m_tid;
724-
823+
725824
if (tid != currentTid && !m_debugger->IsRunning())
726825
m_debugger->SetActiveThread(tid);
727826

ui/threadframes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ private slots:
197197
void resumeThread();
198198
void makeItSoloThread();
199199
void copy();
200+
void copyCurrentFrame();
201+
void copyAllFrames();
200202
};
201203

202204
class ThreadFramesContainer : public SidebarWidget

0 commit comments

Comments
 (0)