diff --git a/notes/README.forks b/notes/README.forks new file mode 100644 index 0000000..d9ab49a --- /dev/null +++ b/notes/README.forks @@ -0,0 +1,28 @@ +When executing a fork() or vfork() call. + + ^running + *running,thread-id="all" + + =thread-group-added,id="i2" + =thread-group-started,id="i2",pid="24125" + =thread-created,id="2",group-id="i2" + + =library-loaded,id="/usr/lib64/libstdc++.so.6",target-name="/usr/lib64/libstdc++.so.6",host-name="/usr/lib64/libstdc++.so.6",symbols-loaded="0",thread-group="i2",ranges=[{from="0x00007ffff7a63870",to="0x00007ffff7b558e8"}] + =library-loaded,id="/lib64/libm.so.6",target-name="/lib64/libm.so.6",host-name="/lib64/libm.so.6",symbols-loaded="0",thread-group="i2",ranges=[{from="0x00007ffff76873a0",to="0x00007ffff7723d95"}] + =library-loaded,id="/lib64/libgcc_s.so.1",target-name="/lib64/libgcc_s.so.1",host-name="/lib64/libgcc_s.so.1",symbols-loaded="0",thread-group="i2",ranges=[{from="0x00007ffff745e010",to="0x00007ffff746f365"}] + =library-loaded,id="/lib64/libc.so.6",target-name="/lib64/libc.so.6",host-name="/lib64/libc.so.6",symbols-loaded="0",thread-group="i2",ranges=[{from="0x00007ffff709aaf0",to="0x00007ffff7200afc"}] + =library-loaded,id="/lib64/ld-linux-x86-64.so.2",target-name="/lib64/ld-linux-x86-64.so.2",host-name="/lib64/ld-linux-x86-64.so.2",symbols-loaded="0",thread-group="i2",ranges=[{from="0x00007ffff7dd4170",to="0x00007ffff7df4090"}] + + *stopped,reason="end-stepping-range",frame={addr="0x0000000000400702",func="main",args=[],file="helloinferior.c",fullname="/home/erniep/Development/Peak/src/seer/tests/helloinferior/helloinferior.c",line="11",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="0" + + + ^running + *running,thread-id="all" + =thread-group-added,id="i2" + =thread-group-started,id="i2",pid="11514" + =thread-created,id="2",group-id="i2" + *running,thread-id="2" + *stopped,reason="end-stepping-range",frame={addr="0x0000000000400702",func="main",args=[],file="hellovfork.c",fullname="/home/erniep/Development/Peak/src/seer/tests/hellovfork/hellovfork.c",line="11",arch="i386:x86-64"},thread-id="2",stopped-threads="all",core="4" + + + diff --git a/src/SeerGdbWidget.cpp b/src/SeerGdbWidget.cpp index 4e599ef..ddd539c 100644 --- a/src/SeerGdbWidget.cpp +++ b/src/SeerGdbWidget.cpp @@ -137,6 +137,9 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(_gdbMonitor, &GdbMonitor::astrixTextOutput, this, &SeerGdbWidget::handleText); QObject::connect(_gdbMonitor, &GdbMonitor::equalTextOutput, this, &SeerGdbWidget::handleText); + QObject::connect(_gdbMonitor, &GdbMonitor::equalTextOutput, threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::handleText); + QObject::connect(_gdbMonitor, &GdbMonitor::astrixTextOutput, threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::handleText); + QObject::connect(editorManagerWidget, &SeerEditorManagerWidget::refreshBreakpointsList, this, &SeerGdbWidget::handleGdbGenericpointList); QObject::connect(editorManagerWidget, &SeerEditorManagerWidget::refreshStackFrames, this, &SeerGdbWidget::handleGdbStackListFrames); QObject::connect(editorManagerWidget, &SeerEditorManagerWidget::insertBreakpoint, this, &SeerGdbWidget::handleGdbBreakpointInsert); @@ -202,6 +205,7 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::refreshThreadFrames, this, &SeerGdbWidget::handleGdbThreadListFrames); QObject::connect(threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::selectedFile, editorManagerWidget, &SeerEditorManagerWidget::handleOpenFile); QObject::connect(threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::selectedThread, this, &SeerGdbWidget::handleGdbThreadSelectId); + QObject::connect(threadManagerWidget, &SeerThreadManagerWidget::forkFollowsModeChanged, this, &SeerGdbWidget::handleGdbForkFollowMode); QObject::connect(_breakpointsBrowserWidget, &SeerBreakpointsBrowserWidget::refreshBreakpointsList, this, &SeerGdbWidget::handleGdbGenericpointList); QObject::connect(_breakpointsBrowserWidget, &SeerBreakpointsBrowserWidget::deleteBreakpoints, this, &SeerGdbWidget::handleGdbBreakpointDelete); @@ -2496,6 +2500,26 @@ void SeerGdbWidget::setAssemblySymbolDemagling (const QString& onoff) { } } +void SeerGdbWidget::handleGdbForkFollowMode (QString mode) { + + if (executableLaunchMode() == "") { + return; + } + + if (mode == "Parent") { + handleGdbCommand("-gdb-set follow-fork-mode parent"); + handleGdbCommand("-gdb-set detach-on-fork on"); + }else if (mode == "Child") { + handleGdbCommand("-gdb-set follow-fork-mode child"); + handleGdbCommand("-gdb-set detach-on-fork on"); + }else if (mode == "Both") { + handleGdbCommand("-gdb-set follow-fork-mode parent"); + handleGdbCommand("-gdb-set detach-on-fork off"); + }else{ + qWarning() << "Invalid 'ForkFollowMode' of '" << mode << "'"; + } +} + QString SeerGdbWidget::assemblySymbolDemagling () const { return _assemblySymbolDemangling; diff --git a/src/SeerGdbWidget.h b/src/SeerGdbWidget.h index 08215c9..8a6deae 100644 --- a/src/SeerGdbWidget.h +++ b/src/SeerGdbWidget.h @@ -247,6 +247,7 @@ class SeerGdbWidget : public QWidget, protected Ui::SeerGdbWidgetForm { void handleGdbSaveBreakpoints (); void handleGdbAssemblyDisassemblyFlavor (); void handleGdbAssemblySymbolDemangling (); + void handleGdbForkFollowMode (QString mode); void handleGdbProcessFinished (int exitCode, QProcess::ExitStatus exitStatus); void handleGdbProcessErrored (QProcess::ProcessError errorStatus); diff --git a/src/SeerThreadFramesBrowserWidget.cpp b/src/SeerThreadFramesBrowserWidget.cpp index a1846f0..c8b5cdb 100644 --- a/src/SeerThreadFramesBrowserWidget.cpp +++ b/src/SeerThreadFramesBrowserWidget.cpp @@ -152,6 +152,20 @@ void SeerThreadFramesBrowserWidget::handleText (const QString& text) { } } + }else if (text.startsWith("*running,thread-id=")) { + + refresh(); + + }else if (text.startsWith("=thread-created,id=")) { + + // =thread-created,id="2",group-id="i2" + refresh(); + + }else if (text.startsWith("=thread-exited,id=")) { + + // =thread-exited,id="2",group-id="i2" + refresh(); + }else if (text.startsWith("^error,msg=\"No registers.\"")) { threadTreeWidget->clear(); diff --git a/src/SeerThreadManagerWidget.cpp b/src/SeerThreadManagerWidget.cpp index ab95712..a179c57 100644 --- a/src/SeerThreadManagerWidget.cpp +++ b/src/SeerThreadManagerWidget.cpp @@ -26,7 +26,8 @@ SeerThreadManagerWidget::SeerThreadManagerWidget (QWidget* parent) : QWidget(par tabWidget->setCornerWidget(refreshToolButton, Qt::TopRightCorner); // Connect things. - QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerThreadManagerWidget::handleRefreshToolButtonClicked); + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerThreadManagerWidget::handleRefreshToolButtonClicked); + QObject::connect(forkFollowsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &SeerThreadManagerWidget::handleForkFollowComboBox); } SeerThreadManagerWidget::~SeerThreadManagerWidget () { @@ -40,9 +41,26 @@ SeerThreadFramesBrowserWidget* SeerThreadManagerWidget::threadFramesBrowserWidge return _threadFramesBrowserWidget; } +void SeerThreadManagerWidget::setForkFollowsMode (const QString& mode) { + + forkFollowsComboBox->setCurrentText(mode); +} + +QString SeerThreadManagerWidget::forkFollowsMode () const { + + return forkFollowsComboBox->currentText(); +} + void SeerThreadManagerWidget::handleRefreshToolButtonClicked () { threadFramesBrowserWidget()->refresh(); threadIdsBrowserWidget()->refresh(); } +void SeerThreadManagerWidget::handleForkFollowComboBox (int index) { + + Q_UNUSED(index); + + emit forkFollowsModeChanged(forkFollowsComboBox->currentText()); +} + diff --git a/src/SeerThreadManagerWidget.h b/src/SeerThreadManagerWidget.h index 3834652..0308199 100644 --- a/src/SeerThreadManagerWidget.h +++ b/src/SeerThreadManagerWidget.h @@ -19,9 +19,17 @@ class SeerThreadManagerWidget : public QWidget, protected Ui::SeerThreadManagerW SeerThreadFramesBrowserWidget* threadFramesBrowserWidget (); signals: + void forkFollowsModeChanged (const QString& mode); + + public slots: + void setForkFollowsMode (const QString& mode); + QString forkFollowsMode () const; + + private slots: void handleRefreshToolButtonClicked (); + void handleForkFollowComboBox (int index); private: SeerThreadIdsBrowserWidget* _threadIdsBrowserWidget; diff --git a/src/SeerThreadManagerWidget.ui b/src/SeerThreadManagerWidget.ui index 5f62d4a..aefc1fb 100644 --- a/src/SeerThreadManagerWidget.ui +++ b/src/SeerThreadManagerWidget.ui @@ -33,6 +33,49 @@ + + + + + + Fork follows: + + + + + + + + Parent + + + + + Child + + + + + Both + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/tests/helloexecl/.gitignore b/tests/helloexecl/.gitignore new file mode 100644 index 0000000..7fcd127 --- /dev/null +++ b/tests/helloexecl/.gitignore @@ -0,0 +1,2 @@ +hellocaller +hellocallee diff --git a/tests/helloexecl/Makefile b/tests/helloexecl/Makefile new file mode 100644 index 0000000..052489e --- /dev/null +++ b/tests/helloexecl/Makefile @@ -0,0 +1,16 @@ +# This is the default target, which will be built when +# you invoke make +.PHONY: all +all: hellocaller hellocallee + +hellocaller: hellocaller.c + g++ -g -o hellocaller hellocaller.c + +hellocallee: hellocallee.c + g++ -g -o hellocallee hellocallee.c + +.PHONY: clean +clean: + rm -f hellocaller hellocaller.o + rm -f hellocallee hellocallee.o + diff --git a/tests/helloexecl/hellocallee.c b/tests/helloexecl/hellocallee.c new file mode 100644 index 0000000..f1b8957 --- /dev/null +++ b/tests/helloexecl/hellocallee.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char *argv[0]) { + + for (int i = 1; i < argc; ++i) { + if (i != 1) printf(" "); + printf("%s", argv[i]); + } + + printf("\n"); + + return 0; +} + diff --git a/tests/helloexecl/hellocaller.c b/tests/helloexecl/hellocaller.c new file mode 100644 index 0000000..fba1de0 --- /dev/null +++ b/tests/helloexecl/hellocaller.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include + +int main() { + + pid_t pid = fork(); + int status = 0; + + if (pid == 0) { + + execlp("./hellocallee", "echo", "aa", "bb", (char*)0); + abort(); + + }else{ + + printf("parent %d waiting for %d\n", getpid(), pid); + waitpid(pid, &status, 0); + printf("child %d exited %d\n", pid, status); + + } + + return status; +} diff --git a/tests/helloinferior/helloinferior.c b/tests/helloinferior/helloinferior.c index 7245409..f1311ff 100644 --- a/tests/helloinferior/helloinferior.c +++ b/tests/helloinferior/helloinferior.c @@ -1,7 +1,36 @@ #include +#include #include +#include +#include int main () { + pid_t pid = fork(); - printf("Hello!\n"); + + if (pid == 0) { + printf("Hello from child! (%d)\n", getpid()); + sleep(10); + exit(EXIT_SUCCESS); + } + + // To Here and see the difference + printf("Hello from parent! (%d)\n", getpid()); + + // Comment from here to... + // Parent waits process pid (child) + int status; + + waitpid(pid, &status, 0); + + // Option is 0 since I check it later + if (WIFSIGNALED(status)){ + printf("Error\n"); + + }else if (WEXITSTATUS(status)){ + printf("Child exited normally.\n"); + } + + return 0; } + diff --git a/tests/hellovfork/.gitignore b/tests/hellovfork/.gitignore new file mode 100644 index 0000000..d0aaf10 --- /dev/null +++ b/tests/hellovfork/.gitignore @@ -0,0 +1 @@ +hellocfork diff --git a/tests/hellovfork/Makefile b/tests/hellovfork/Makefile new file mode 100644 index 0000000..c4d2c60 --- /dev/null +++ b/tests/hellovfork/Makefile @@ -0,0 +1,12 @@ +# This is the default target, which will be built when +# you invoke make +.PHONY: all +all: hellovfork + +hellovfork: hellovfork.c + g++ -g -o hellovfork hellovfork.c + +.PHONY: clean +clean: + rm -f hellovfork hellovfork.o + diff --git a/tests/hellovfork/hellovfork b/tests/hellovfork/hellovfork new file mode 100755 index 0000000..a5e742c Binary files /dev/null and b/tests/hellovfork/hellovfork differ diff --git a/tests/hellovfork/hellovfork.c b/tests/hellovfork/hellovfork.c new file mode 100644 index 0000000..74a07ad --- /dev/null +++ b/tests/hellovfork/hellovfork.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +int main () { + + pid_t pid = vfork(); + + if (pid == 0) { + printf("Hello from child! (%d)\n", getpid()); + sleep(2); + exit(EXIT_SUCCESS); + } + + // To Here and see the difference + printf("Hello from parent! (%d)\n", getpid()); + + // Comment from here to... + // Parent waits process pid (child) + int status; + + waitpid(pid, &status, 0); + + // Option is 0 since I check it later + if (WIFSIGNALED(status)){ + printf("Error\n"); + + }else if (WEXITSTATUS(status)){ + printf("Child exited normally.\n"); + } + + return 0; +} +