"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.10.3/src/widgets/DebugActions.cpp" (8 May 2020, 13945 Bytes) of package /linux/privat/cutter-1.10.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "DebugActions.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.10.2_vs_1.10.3.

    1 #include "DebugActions.h"
    2 #include "core/MainWindow.h"
    3 #include "dialogs/AttachProcDialog.h"
    4 #include "dialogs/NativeDebugDialog.h"
    5 #include "common/Configuration.h"
    6 #include "common/Helpers.h"
    7 
    8 #include <QPainter>
    9 #include <QMenu>
   10 #include <QList>
   11 #include <QFileInfo>
   12 #include <QToolBar>
   13 #include <QToolButton>
   14 
   15 DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
   16     QObject(main),
   17     main(main)
   18 {
   19     setObjectName("DebugActions");
   20     // setIconSize(QSize(16, 16));
   21 
   22     // define icons
   23     QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg");
   24     QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg");
   25     QIcon startRemoteIcon = QIcon(":/img/icons/play_light_remote.svg");
   26     stopIcon = QIcon(":/img/icons/media-stop_light.svg");
   27     restartIcon = QIcon(":/img/icons/spin_light.svg");
   28     detachIcon = QIcon(":/img/icons/detach_debugger.svg");
   29     startDebugIcon = QIcon(":/img/icons/play_light_debug.svg");
   30     continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg");
   31     suspendIcon = QIcon(":/img/icons/media-suspend_light.svg");
   32 
   33     // define action labels
   34     QString startEmulLabel = tr("Start emulation");
   35     QString startAttachLabel = tr("Attach to process");
   36     QString startRemoteLabel = tr("Connect to a remote debugger");
   37     QString stopDebugLabel = tr("Stop debug");
   38     QString stopEmulLabel = tr("Stop emulation");
   39     QString restartEmulLabel = tr("Restart emulation");
   40     QString continueUMLabel = tr("Continue until main");
   41     QString continueUCLabel = tr("Continue until call");
   42     QString continueUSLabel = tr("Continue until syscall");
   43     QString stepLabel = tr("Step");
   44     QString stepOverLabel = tr("Step over");
   45     QString stepOutLabel = tr("Step out");
   46     suspendLabel = tr("Suspend the process");
   47     continueLabel = tr("Continue");
   48     restartDebugLabel = tr("Restart program");
   49     startDebugLabel = tr("Start debug");
   50 
   51     // define actions
   52     actionStart = new QAction(startDebugIcon, startDebugLabel, this);
   53     actionStart->setShortcut(QKeySequence(Qt::Key_F9));
   54     actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this);
   55     actionAttach = new QAction(startAttachIcon, startAttachLabel, this);
   56     actionStartRemote = new QAction(startRemoteIcon, startRemoteLabel, this);
   57     actionStop = new QAction(stopIcon, stopDebugLabel, this);
   58     actionContinue = new QAction(continueIcon, continueLabel, this);
   59     actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
   60     actionContinueUntilMain = new QAction(continueUMLabel, this);
   61     actionContinueUntilCall = new QAction(continueUCLabel, this);
   62     actionContinueUntilSyscall = new QAction(continueUSLabel, this);
   63     actionStep = new QAction(stepLabel, this);
   64     actionStep->setShortcut(QKeySequence(Qt::Key_F7));
   65     actionStepOver = new QAction(stepOverLabel, this);
   66     actionStepOver->setShortcut(QKeySequence(Qt::Key_F8));
   67     actionStepOut = new QAction(stepOutLabel, this);
   68     actionStepOut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F8));
   69 
   70     QToolButton *startButton = new QToolButton;
   71     startButton->setPopupMode(QToolButton::MenuButtonPopup);
   72     connect(startButton, &QToolButton::triggered, startButton, &QToolButton::setDefaultAction);
   73     QMenu *startMenu = new QMenu(startButton);
   74 
   75     // only emulation is currently allowed
   76     startMenu->addAction(actionStart);
   77     startMenu->addAction(actionStartEmul);
   78     startMenu->addAction(actionAttach);
   79     startMenu->addAction(actionStartRemote);
   80     startButton->setDefaultAction(actionStart);
   81     startButton->setMenu(startMenu);
   82 
   83     continueUntilButton = new QToolButton;
   84     continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup);
   85     connect(continueUntilButton, &QToolButton::triggered, continueUntilButton,
   86             &QToolButton::setDefaultAction);
   87     QMenu *continueUntilMenu = new QMenu(continueUntilButton);
   88     continueUntilMenu->addAction(actionContinueUntilMain);
   89     continueUntilMenu->addAction(actionContinueUntilCall);
   90     continueUntilMenu->addAction(actionContinueUntilSyscall);
   91     continueUntilButton->setMenu(continueUntilMenu);
   92     continueUntilButton->setDefaultAction(actionContinueUntilMain);
   93 
   94     // define toolbar widgets and actions
   95     toolBar->addWidget(startButton);
   96     toolBar->addAction(actionContinue);
   97     toolBar->addAction(actionStop);
   98     actionAllContinues = toolBar->addWidget(continueUntilButton);
   99     toolBar->addAction(actionStepOver);
  100     toolBar->addAction(actionStep);
  101     toolBar->addAction(actionStepOut);
  102 
  103     allActions = {actionStop, actionAllContinues, actionContinue, actionContinueUntilCall, actionContinueUntilMain, actionContinueUntilSyscall, actionStep, actionStepOut, actionStepOver};
  104     // hide allactions
  105     setAllActionsVisible(false);
  106 
  107     // Toggle all buttons except restart, suspend(=continue) and stop since those are
  108     // necessary to avoid staying stuck
  109     toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain,
  110         actionContinueUntilCall, actionContinueUntilSyscall};
  111     toggleConnectionActions = {actionAttach, actionStartRemote};
  112 
  113     connect(Core(), &CutterCore::debugProcessFinished, this, [ = ](int pid) {
  114         QMessageBox msgBox;
  115         msgBox.setText(tr("Debugged process exited (") + QString::number(pid) + ")");
  116         msgBox.exec();
  117     });
  118 
  119     connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() {
  120         bool disableToolbar = Core()->isDebugTaskInProgress();
  121         if (Core()->currentlyDebugging) {
  122             for (QAction *a : toggleActions) {
  123                 a->setDisabled(disableToolbar);
  124             }
  125             // Suspend should only be available when other icons are disabled
  126             if (disableToolbar) {
  127                 actionContinue->setText(suspendLabel);
  128                 actionContinue->setIcon(suspendIcon);
  129             } else {
  130                 actionContinue->setText(continueLabel);
  131                 actionContinue->setIcon(continueIcon);
  132             }
  133         } else {
  134             for (QAction *a : toggleConnectionActions) {
  135                 a->setDisabled(disableToolbar);
  136             }
  137         }
  138     });
  139 
  140     connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug);
  141     connect(actionStop, &QAction::triggered, [ = ]() {
  142         actionStart->setVisible(true);
  143         actionStartEmul->setVisible(true);
  144         actionAttach->setVisible(true);
  145         actionStartRemote->setVisible(true);
  146         actionStop->setText(stopDebugLabel);
  147         actionStop->setIcon(stopIcon);
  148         actionStart->setText(startDebugLabel);
  149         actionStart->setIcon(startDebugIcon);
  150         actionStartEmul->setText(startEmulLabel);
  151         actionStartEmul->setIcon(startEmulIcon);
  152         continueUntilButton->setDefaultAction(actionContinueUntilMain);
  153         setAllActionsVisible(false);
  154     });
  155     connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
  156     connect(actionStart, &QAction::triggered, this, &DebugActions::startDebug);
  157 
  158     connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog);
  159     connect(actionStartRemote, &QAction::triggered, this, &DebugActions::attachRemoteDialog);
  160     connect(Core(), &CutterCore::attachedRemote, this, &DebugActions::onAttachedRemoteDebugger);
  161     connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation);
  162     connect(actionStartEmul, &QAction::triggered, [ = ]() {
  163         setAllActionsVisible(true);
  164         actionStart->setVisible(false);
  165         actionAttach->setVisible(false);
  166         actionStartRemote->setVisible(false);
  167         actionContinueUntilMain->setVisible(false);
  168         actionStepOut->setVisible(false);
  169         continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
  170         actionStartEmul->setText(restartEmulLabel);
  171         actionStartEmul->setIcon(restartIcon);
  172         actionStop->setText(stopEmulLabel);
  173     });
  174     connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
  175     connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug);
  176     connect(actionContinueUntilMain, &QAction::triggered, this, &DebugActions::continueUntilMain);
  177     connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall);
  178     connect(actionContinueUntilSyscall, &QAction::triggered, Core(), &CutterCore::continueUntilSyscall);
  179     connect(actionContinue, &QAction::triggered, Core(), [=]() {
  180         // Switch between continue and suspend depending on the debugger's state
  181         if (Core()->isDebugTaskInProgress()) {
  182             Core()->suspendDebug();
  183         } else {
  184             Core()->continueDebug();
  185         }
  186     });
  187 
  188     connect(Config(), &Configuration::interfaceThemeChanged, this, &DebugActions::chooseThemeIcons);
  189     chooseThemeIcons();
  190 }
  191 
  192 void DebugActions::setButtonVisibleIfMainExists()
  193 {
  194     // Use cmd because cmdRaw would not handle multiple commands concatenated
  195     int mainExists = Core()->cmd("f?sym.main; ??").toInt();
  196     // if main is not a flag we hide the continue until main button
  197     if (!mainExists) {
  198         actionContinueUntilMain->setVisible(false);
  199         continueUntilButton->setDefaultAction(actionContinueUntilCall);
  200     }
  201 }
  202 
  203 void DebugActions::showDebugWarning()
  204 {
  205     if (!acceptedDebugWarning) {
  206         acceptedDebugWarning = true;
  207         QMessageBox msgBox;
  208         msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
  209         msgBox.setText(tr("Debug is currently in beta.\n") +
  210             tr("If you encounter any problems or have suggestions, please submit an issue to https://github.com/radareorg/cutter/issues"));
  211         msgBox.exec();
  212     }
  213 }
  214 
  215 void DebugActions::continueUntilMain()
  216 {
  217     QString mainAddr = Core()->cmdRaw("?v sym.main");
  218     Core()->continueUntilDebug(mainAddr);
  219 }
  220 
  221 void DebugActions::attachRemoteDebugger()
  222 {
  223     QString stopAttachLabel = tr("Detach from process");
  224     // Hide unwanted buttons
  225     setAllActionsVisible(true);
  226     actionStart->setVisible(false);
  227     actionStartRemote->setVisible(false);
  228     actionStartEmul->setVisible(false);
  229     actionStop->setText(stopAttachLabel);
  230 }
  231 
  232 void DebugActions::onAttachedRemoteDebugger(bool successfully)
  233 {
  234     if (!successfully) {
  235         QMessageBox msgBox;
  236         msgBox.setText(tr("Error connecting."));
  237         msgBox.exec();
  238         attachRemoteDialog();
  239     } else {
  240         delete remoteDialog;
  241         remoteDialog = nullptr;
  242         attachRemoteDebugger();
  243     }
  244 }
  245 
  246 void DebugActions::attachRemoteDialog()
  247 {
  248     showDebugWarning();
  249 
  250     if (!remoteDialog) {
  251         remoteDialog = new RemoteDebugDialog(main);
  252     }
  253     QMessageBox msgBox;
  254     bool success = false;
  255     while (!success) {
  256         success = true;
  257         if (remoteDialog->exec()) {
  258             if (!remoteDialog->validate()) {
  259                 success = false;
  260                 continue;
  261             }
  262 
  263             Core()->attachRemote(remoteDialog->getUri());
  264         }
  265     }
  266 }
  267 
  268 void DebugActions::attachProcessDialog()
  269 {
  270     showDebugWarning();
  271 
  272     AttachProcDialog dialog(main);
  273     bool success = false;
  274     while (!success) {
  275         success = true;
  276         if (dialog.exec()) {
  277             int pid = dialog.getPID();
  278             if (pid >= 0) {
  279                 attachProcess(pid);
  280             } else {
  281                 success = false;
  282                 QMessageBox msgBox;
  283                 msgBox.setText(tr("Error attaching. No process selected!"));
  284                 msgBox.exec();
  285             }
  286         }
  287     }
  288 }
  289 
  290 void DebugActions::attachProcess(int pid)
  291 {
  292     QString stopAttachLabel = tr("Detach from process");
  293     // hide unwanted buttons
  294     setAllActionsVisible(true);
  295     actionStart->setVisible(false);
  296     actionStartRemote->setVisible(false);
  297     actionStartEmul->setVisible(false);
  298     actionStop->setText(stopAttachLabel);
  299     actionStop->setIcon(detachIcon);
  300     // attach
  301     Core()->attachDebug(pid);
  302 }
  303 
  304 void DebugActions::startDebug()
  305 {
  306     // check if file is executable before starting debug
  307     QString filename = Core()->getConfig("file.path");
  308 
  309     QFileInfo info(filename);
  310     if (!Core()->currentlyDebugging && !info.isExecutable()) {
  311         QMessageBox msgBox;
  312         msgBox.setText(tr("File '%1' does not have executable permissions.").arg(filename));
  313         msgBox.exec();
  314         return;
  315     }
  316 
  317     showDebugWarning();
  318 
  319     NativeDebugDialog dialog(main);
  320     dialog.setArgs(Core()->getConfig("dbg.args"));
  321     QString args;
  322     if (dialog.exec()) {
  323         args = dialog.getArgs();
  324     } else {
  325         return;
  326     }
  327 
  328     // Update dbg.args with the new args
  329     Core()->setConfig("dbg.args", args);
  330 
  331     setAllActionsVisible(true);
  332     actionAttach->setVisible(false);
  333     actionStartRemote->setVisible(false);
  334     actionStartEmul->setVisible(false);
  335     actionStart->setText(restartDebugLabel);
  336     actionStart->setIcon(restartIcon);
  337     setButtonVisibleIfMainExists();
  338 
  339     Core()->startDebug();
  340 }
  341 
  342 void DebugActions::setAllActionsVisible(bool visible)
  343 {
  344     for (QAction *action : allActions) {
  345         action->setVisible(visible);
  346     }
  347 }
  348 
  349 /**
  350  * @brief When theme changed, change icons which have a special version for the theme.
  351  */
  352 void DebugActions::chooseThemeIcons()
  353 {
  354     // List of QActions which have alternative icons in different themes
  355     const QList<QPair<void*, QString>> kSupportedIconsNames {
  356         { actionStep, QStringLiteral("step_into.svg") },
  357         { actionStepOver, QStringLiteral("step_over.svg") },
  358         { actionStepOut, QStringLiteral("step_out.svg") },
  359         { actionContinueUntilMain, QStringLiteral("continue_until_main.svg") },
  360         { actionContinueUntilCall, QStringLiteral("continue_until_call.svg") },
  361         { actionContinueUntilSyscall, QStringLiteral("continue_until_syscall.svg") },
  362     };
  363 
  364 
  365     // Set the correct icon for the QAction
  366     qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {
  367         static_cast<QAction*>(obj)->setIcon(icon);
  368     });
  369 }