Add missing space to log message
[frida/frida.git] / src / gui / Mainwindow.cxx
1 #include "Mainwindow.hxx"
2 #include "qt.hxx"
3 #include "disassembler/llvm/LLVMDisassembler.hxx"
4
5 #include "widgets/CFGScene.hxx"
6 #include "dialogs/NewFunctionDialog.hxx"
7 #include "dialogs/RenameFunctionDialog.hxx"
8
9 #include <sstream>
10
11 namespace {
12 BasicBlockWidget *
13 local__add_basic_block(BasicBlock * block, Disassembler * dis,
14 Mainwindow * mainwindow,
15 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
16 CFGScene * scene, uint64_t starty, uint64_t startx);
17 }
18
19 Mainwindow::Mainwindow(InformationManager* mgr)
20 : manager(mgr)
21 , logger(log4cxx::Logger::getLogger("Mainwindow")) {
22 openAction = new QAction(tr("&Open"), this);
23 // saveAction = new QAction(tr("&Save"), this);
24 exitAction = new QAction(tr("E&xit"), this);
25
26 connect(openAction, SIGNAL(triggered()),
27 this, SLOT(open()));
28 // connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
29 connect(exitAction, SIGNAL(triggered()),
30 qApp, SLOT(quit()));
31
32 fileMenu = menuBar()->addMenu(tr("&File"));
33 fileMenu->addAction(openAction);
34 // fileMenu->addAction(saveAction);
35 fileMenu->addSeparator();
36 fileMenu->addAction(exitAction);
37
38 scripting = new ScriptingDock(tr("Scripting"), this);
39 scripting->setAllowedAreas(Qt::BottomDockWidgetArea);
40 addDockWidget(Qt::BottomDockWidgetArea, scripting);
41
42 listWidget = new QListWidget();
43 listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
44 connect(listWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
45 this, SLOT(showListContextMenu(const QPoint&)));
46
47 stackedWidget = new QStackedWidget();
48 dockWidget = new QDockWidget(tr("Functions"), this);
49 dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea |
50 Qt::RightDockWidgetArea);
51 dockWidget->setWidget(listWidget);
52 addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
53 setCentralWidget(stackedWidget);
54
55 connect(listWidget, SIGNAL(currentRowChanged(int)),
56 this, SLOT(switchMainPlane(int)));
57
58 setWindowTitle(tr("FRIDA"));
59
60 mgr->connect_new_function_signal([&] (Function* fun) {addFunction(fun);});
61 mgr->connect_new_dyn_symbol_signal([&] (const std::string& name) {
62 auto item = new QListWidgetItem(name.c_str(), listWidget);
63 item->setBackground(QBrush(QColor(0xff, 0xdd, 0xdd)));
64 });
65 setGlobalHotkeys();
66 }
67
68 void Mainwindow::setGlobalHotkeys() {
69 QShortcut *shortcut = new QShortcut(QKeySequence("f"), this);
70 connect(shortcut, &QShortcut::activated, this, &Mainwindow::requestNewFunction);
71
72 shortcut = new QShortcut(QKeySequence("r"), listWidget);
73 connect(shortcut, &QShortcut::activated, [=]() {
74 QListWidgetItem * item = listWidget->currentItem();
75 if (item) renameFunction(item);
76 });
77 }
78
79 void Mainwindow::quit()
80 {
81 QMessageBox messageBox;
82 messageBox.setWindowTitle(tr("Frida"));
83 messageBox.setText(tr("Do you really want to quit?"));
84 messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
85 messageBox.setDefaultButton(QMessageBox::No);
86 if (messageBox.exec() == QMessageBox::Yes)
87 qApp->quit();
88 }
89
90 void Mainwindow::open() {
91 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
92 tr("Binaries (*)"));
93
94 manager->reset(fileName.toStdString());
95 }
96
97 void Mainwindow::switchMainPlaneToAddress(uint64_t address) {
98 if (objects_list_by_address.find(address) != objects_list_by_address.end()) {
99 LOG4CXX_DEBUG(logger, "Switching to function " << std::hex << address);
100 QListWidgetItem * item = objects_list_by_address[address];
101 listWidget->setCurrentItem(item);
102 stackedWidget->setCurrentWidget(objects_list[item]);
103 } else {
104 LOG4CXX_DEBUG(logger, "No function at " << std::hex << address
105 << " -- it's probably an imported Symbol");
106 }
107 }
108
109 void Mainwindow::switchMainPlane(int index) {
110 stackedWidget->setCurrentWidget(objects_list[listWidget->currentItem()]);
111 }
112
113 void Mainwindow::showListContextMenu(const QPoint& point) {
114 QListWidgetItem * item = listWidget->itemAt(point);
115 QMenu menu(this);
116 if (item) {
117 QAction * act = menu.addAction("Rename Function");
118 connect(act, &QAction::triggered, [=]() {this->renameFunction(item);});
119 } else {
120 QAction * act = menu.addAction("AddFunction");
121 connect(act, SIGNAL(triggered()), this, SLOT(requestNewFunction()));
122 }
123 menu.exec(listWidget->mapToGlobal(point));
124 }
125
126 void Mainwindow::requestNewFunction() {
127 NewFunctionDialog dialog;
128 int result = dialog.exec();
129 if (QDialog::Accepted == result) {
130 LOG4CXX_DEBUG(logger, "requesting Function at " << std::hex << dialog.result());
131 manager->getDisassembler()->disassembleFunctionAt(dialog.result());
132 } else {
133 LOG4CXX_DEBUG(logger, "requestNewFunction aborted");
134 }
135 }
136
137 void Mainwindow::renameFunction(QListWidgetItem * item) {
138 RenameFunctionDialog dialog;
139 int result = dialog.exec();
140 if (QDialog::Accepted == result) {
141 LOG4CXX_DEBUG(logger, "renaming Function " << item->text().toStdString()
142 << " to " << dialog.result().toStdString());
143 item->setText(dialog.result());
144 } else {
145 LOG4CXX_DEBUG(logger, "renameFunction aborted");
146 }
147 }
148
149 void Mainwindow::addFunction(Function* fun) {
150 if (functions.find(fun) != functions.end())
151 return;
152
153 functions.insert(fun);
154
155 QTabWidget * w = new QTabWidget();
156
157 // CFG
158 CFGScene * scene = new CFGScene;
159
160 Disassembler * dis = manager->getDisassembler();
161 BasicBlock * block = dis->getBasicBlock(fun->getStartAddress());
162
163 uint64_t start_address(std::numeric_limits<uint64_t>::max());
164 for (auto b : fun->blocks()) {
165 if (b.first < start_address)
166 start_address = b.first;
167 }
168
169 local__add_basic_block(block, manager->getDisassembler(), this,
170 blocks, scene, start_address, 100);
171
172 QGraphicsView * view = new QGraphicsView(scene);
173 w->addTab(view, "CFG");
174
175 // Listing
176 QTableWidget * t = new QTableWidget();
177 t->setColumnCount(3);
178 t->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
179
180 w->addTab(t, "Listing");
181
182 QListWidgetItem * item = new QListWidgetItem(fun->getName().c_str(), listWidget);
183 stackedWidget->addWidget(w);
184 objects_list.insert(std::make_pair(item, w));
185 LOG4CXX_DEBUG(logger, "Adding function widget at " << std::hex
186 << fun->getStartAddress());
187 objects_list_by_address.insert(std::make_pair(fun->getStartAddress(), item));
188 }
189
190 namespace {
191 BasicBlockWidget *
192 local__add_basic_block(BasicBlock * block, Disassembler * dis,
193 Mainwindow * mainwindow,
194 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
195 CFGScene * scene, uint64_t starty, uint64_t startx) {
196
197 decltype(known_blocks.begin()) old;
198 if ((old = known_blocks.find(block->getStartAddress())) != known_blocks.end())
199 return old->second;
200
201 std::stringstream s;
202 s << "BLOCK_" << std::hex << block->getStartAddress()
203 << "_" << block->getEndAddress();
204 BasicBlockWidget * widget = new BasicBlockWidget(s.str().c_str(),
205 block, mainwindow);
206
207 known_blocks.insert(std::make_pair(block->getStartAddress(), widget));
208
209 scene->addItem(widget);
210 widget->setFlag(QGraphicsItem::ItemIsMovable, true);
211 widget->moveBy(100*startx, block->getStartAddress() - starty);
212
213 dis->printEachInstruction(block->getStartAddress(),
214 block->getEndAddress(),
215 [&](uint8_t* bytes,
216 size_t byte_count,
217 const std::string& line,
218 const std::string& ref) {
219 widget->addItem(bytes, byte_count,
220 line.c_str() + 1, // remove \t
221 ref.c_str());
222 });
223
224 BasicBlockWidget *tmp, *nextl(NULL), *nextr(NULL);
225 BasicBlock * tmpblock;
226 if (block->getNextBlock(0) != 0) {
227 int xshift = 0;
228 if (block->getNextBlock(1) != 0)
229 xshift = 1;
230 tmpblock = dis->getBasicBlock(block->getNextBlock(0));
231 tmp = local__add_basic_block(tmpblock, dis,
232 mainwindow,
233 known_blocks,
234 scene, starty, startx+xshift);
235 nextl = tmp;
236 tmp->addPrevious(widget);
237 }
238 if (block->getNextBlock(1) != 0) {
239 tmpblock = dis->getBasicBlock(block->getNextBlock(1));
240 tmp = local__add_basic_block(tmpblock, dis,
241 mainwindow,
242 known_blocks,
243 scene, starty, startx-1);
244 nextr = tmp;
245 tmp->addPrevious(widget);
246 }
247 widget->addNext(nextl, nextr);
248 return widget;
249 }
250 }