Author Topic: debugger plugin: catch the inferior PID under gdb  (Read 36221 times)

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #15 on: April 10, 2012, 04:16:39 pm »
Uff, sorry, the PID is not needed in remote debugging, because in this case gdb expects ctrl-c events for stopping it.

Morten: Can you do some testing of this patch? If I remember correctly you had some debugevent related problems, so this could be your fix. :)
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9694
Re: debugger plugin: catch the inferior PID under gdb
« Reply #16 on: April 10, 2012, 05:33:15 pm »
Morten: Can you do some testing of this patch? If I remember correctly you had some debugevent related problems, so this could be your fix. :)
...which patch? The one applied in trunk already?
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #17 on: April 10, 2012, 05:39:05 pm »
This one, I think: http://forums.codeblocks.org/index.php/topic,16159.msg109312.html#msg109312

ollydbg: Is there a chance to try your patch in a 64bit windows with a 64bit executable?
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5916
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: debugger plugin: catch the inferior PID under gdb
« Reply #18 on: April 11, 2012, 01:50:08 am »
ollydbg: Is there a chance to try your patch in a 64bit windows with a 64bit executable?
Sorry, I do not have a 64bit Windows system nor a 64bit gdb.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5916
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: debugger plugin: catch the inferior PID under gdb
« Reply #19 on: April 20, 2012, 03:25:48 am »
Commit this in rev7936.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: debugger plugin: catch the inferior PID under gdb
« Reply #20 on: January 29, 2016, 06:43:44 pm »
I'm on linux with GDB 7.10, and CB never gets the correct pid for me. This means I can't pause or set any breakpoints after I start debugging. Here's some log:

Quote
[...]
[debug]>>>>>>cb_gdb:
[debug]> run
[debug]Starting program: /home/gokul/Documents/Projects/CodeBlocks/src/devel/codeblocks --debug-log --multiple-instance -ns -ni -v -p debug
[debug][Thread debugging using libthread_db enabled]
[debug]Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".

Child process PID: 14736

[debug][New Thread 0xb5565b40 (LWP 14736)]

[debug][New Thread 0xb4d64b40 (LWP 14737)]
[debug][New Thread 0xb4563b40 (LWP 14738)]
[debug][New Thread 0xb3b0db40 (LWP 14739)]
[debug][New Thread 0xb330cb40 (LWP 14740)]
[debug][New Thread 0xb1966b40 (LWP 14747)]
[debug][New Thread 0xb0fcdb40 (LWP 14801)]
[debug][New Thread 0xb076db40 (LWP 14802)]
[debug][New Thread 0xafec0b40 (LWP 14803)]
[debug][Thread 0xafec0b40 (LWP 14803) exited]
[debug][Thread 0xb0fcdb40 (LWP 14801) exited]
[debug][Thread 0xb076db40 (LWP 14802) exited]
[debug][Thread 0xb1966b40 (LWP 14747) exited]
[debug]Starting Code::Blocks svn build  rev 10703 Jan 29 2016, 03:07:41 - wx2.8.12 (Linux, unicode) - 32 bit
[debug][Thread 0xb4563b40 (LWP 14738) exited]
[debug][Thread 0xb4d64b40 (LWP 14737) exited]
[debug][Thread 0xb5565b40 (LWP 14736) exited]
[debug][Thread 0xb330cb40 (LWP 14740) exited]
[debug][Thread 0xb3b0db40 (LWP 14739) exited]
[debug][Inferior 1 (process 14101) exited normally]
[debug]>>>>>>cb_gdb:

[Inferior 1 (process 14101) exited normally]

[debug]> quit

Debugger finished with status 0

You can see CB tries to get the pid by parsing the thread info, i.e., the line in red:
"[New Thread 0xb5565b40 (LWP 14736)]"
but this is not the correct pid of the inferior process! The correct pid can be seen at the end, in the green line:
"[Inferior 1 (process 14101) exited normally]"

The only proper way to get the pid seems to be to start gdb in "break on entry" mode and run "info program" or "info inferior" at the gdb prompt, parse the result and then continue. I see there is already support for "info program", so why not use that? I'll post a patch in a while...

Edit: Here's the patch which works for me:
Code: diff
From 9f668e2611366fde9e9ee3a8bd17f553bafd30fb Mon Sep 17 00:00:00 2001
From: huki <gk7huki@gmail.com>
Date: Sat, 30 Jan 2016 02:51:50 +0530
Subject: [PATCH] debugger_gdb: [huki] properly get the pid of the inferior
 process

We now break on entry and extract the pid through "info program", then continue if needed.
Fixed functionality of m_ManualBreakOnEntry.
Keep old way of catching pid for remoteDebugging.
---
 src/plugins/debuggergdb/gdb_commands.h |  3 +++
 src/plugins/debuggergdb/gdb_driver.cpp | 10 ++++++++++
 2 files changed, 13 insertions(+)

diff --git a/src/plugins/debuggergdb/gdb_commands.h b/src/plugins/debuggergdb/gdb_commands.h
index 3021cd7..5536c84 100644
--- a/src/plugins/debuggergdb/gdb_commands.h
+++ b/src/plugins/debuggergdb/gdb_commands.h
@@ -722,7 +722,10 @@ class GdbCmd_InfoProgram : public DebuggerCmd
             {
                 long pid;
                 if (pid_str.ToLong(&pid, 10) && pid != 0)
+                {
                     m_pDriver->SetChildPID(pid);
+                    m_pDriver->GetDebugger()->Log(wxString::Format(_("Child process PID: %ld"), pid));
+                }
             }
         }
 };
diff --git a/src/plugins/debuggergdb/gdb_driver.cpp b/src/plugins/debuggergdb/gdb_driver.cpp
index cc1ccd5..b2365c9 100644
--- a/src/plugins/debuggergdb/gdb_driver.cpp
+++ b/src/plugins/debuggergdb/gdb_driver.cpp
@@ -503,6 +503,7 @@ void GDB_driver::Start(bool breakOnEntry)
     {
         m_ManualBreakOnEntry = !remoteDebugging;
         // start the process
+        #if 0
         if (breakOnEntry)
             QueueCommand(new GdbCmd_Start(this, remoteDebugging ? _T("continue") : _T("start")));
         else
@@ -511,6 +512,9 @@ void GDB_driver::Start(bool breakOnEntry)
             m_ManualBreakOnEntry=false;  // must be reset or gdb does not stop at first breakpoint
             QueueCommand(new GdbCmd_Start(this, remoteDebugging ? _T("continue") : _T("run")));
         }
+        #endif
+        // NOTE(huki): use "start" command so we can break on entry and run "info program" to get the pid
+        QueueCommand(new GdbCmd_Start(this, remoteDebugging ? _T("continue") : _T("start")));
         m_IsStarted = true;
     }
 } // Start
@@ -778,6 +782,7 @@ void GDB_driver::ParseOutput(const wxString& output)
 {
     m_Cursor.changed = false;
 
+    // NOTE(huki): this doesn't work on linux with GDB 7 - the pid given in the output is wrong
     if (platform::windows && m_ChildPID == 0)
     {
         if (reChildPid2.Matches(output)) // [New Thread 2684.0xf40] or [New thread 2684.0xf40]
@@ -1043,6 +1048,7 @@ void GDB_driver::ParseOutput(const wxString& output)
                     m_needsUpdate = true;
                 }
             }
+            m_ManualBreakOnEntry = false;   // must be reset or gdb does not stop at first breakpoint
         }
 
         else if (lines[i].StartsWith(wxT("Temporary breakpoint")))
@@ -1177,7 +1183,11 @@ void GDB_driver::HandleMainBreakPoint(const wxRegEx& reBreak_in, wxString line)
             QueueCommand(new GdbCmd_InfoProgram(this), DebuggerDriver::High);
 
         if (m_ManualBreakOnEntry && !m_BreakOnEntry)
+        {
+            m_ManualBreakOnEntry = false;
+            m_ProgramIsStopped = true;
             Continue();
+        }
         else
         {
             m_ManualBreakOnEntry = false;
--
2.5.0


Edit2: Updated the patch with few fixes, also keeps the old way of catching pid as a fallback (eg, for remote debugging).
commit on git
« Last Edit: January 31, 2016, 03:13:43 pm by Huki »

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #21 on: January 30, 2016, 12:15:36 am »
Don't like this patch. I've not looked at the whole of it, just saw the part with the mandatory break-on-entry.
And I don't like it, because it sounds too fragile. I have a patch that uses the system apis to find the pids.
Can you try it? Probably you should disable the pid detection.

Code: diff
From b7a560ac02d6e0f2bedab5a6f02b0fd14875be19 Mon Sep 17 00:00:00 2001
From: T Petrov <tpetrov@codeblocks.org>
Date: Sat, 30 Jan 2016 01:13:12 +0200
Subject: [PATCH] * debugger: Find the debuggee's PID using OS APIs when the
 parsing the log fails

---
 src/include/globals.h                   |  6 ++
 src/plugins/debuggergdb/debuggergdb.cpp | 15 ++++-
 src/sdk/globals.cpp                     | 98 +++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/src/include/globals.h b/src/include/globals.h
index 704924d..b84aafb 100644
--- a/src/include/globals.h
+++ b/src/include/globals.h
@@ -386,4 +386,10 @@ namespace platform
 // one thing that's not checked yet are circular symlinks - watch out!
 extern DLLIMPORT wxString realpath(const wxString& path);

+/** Return a vector full with the PIDs of the child processes of the parent.
+  * @param[out] children List of PIDs of the child processes of the parent
+  * @param[in] parent PID of the parent process
+  */
+extern DLLIMPORT void cbGetChildrenPIDs(std::vector<int> &children, int parent);
+
 #endif // SDK_GLOBALS_H
diff --git a/src/plugins/debuggergdb/debuggergdb.cpp b/src/plugins/debuggergdb/debuggergdb.cpp
index fded9a9..888de11 100644
--- a/src/plugins/debuggergdb/debuggergdb.cpp
+++ b/src/plugins/debuggergdb/debuggergdb.cpp
@@ -1583,7 +1583,20 @@ void DebuggerGDB::DoBreak(bool temporary)
             pid = 0;
         }
         if (pid <= 0)
-            pid = m_Pid; // try poking gdb directly
+        {
+            DebugLog(_("Child PID have not been detected!"), Logger::warning);
+            std::vector<int> childrenPIDs;
+            cbGetChildrenPIDs(childrenPIDs, m_Pid);
+            if (!childrenPIDs.empty())
+            {
+                if (childrenPIDs.size()>1)
+                    DebugLog(_("Found more than one child processes. Use the PID of the first "), Logger::warning);
+                pid = childrenPIDs.front();
+                m_State.GetDriver()->SetChildPID(pid);
+            }
+            else
+                pid = m_Pid; // try poking gdb directly
+        }
         // non-windows gdb can interrupt the running process. yay!
         if (pid <= 0) // look out for the "fake" PIDs (killall)
             cbMessageBox(_("Unable to stop the debug process!"), _("Error"), wxOK | wxICON_WARNING);
diff --git a/src/sdk/globals.cpp b/src/sdk/globals.cpp
index d44d81d..b2b3e52 100644
--- a/src/sdk/globals.cpp
+++ b/src/sdk/globals.cpp
@@ -1453,3 +1453,101 @@ int cbProjectTreeImages::VirtualFolderIconIndex()
 {
     return (int)fvsVirtualFolder;
 }
+
+namespace
+{
+static int ParseParentPID(const char *line)
+{
+    const char *p = strchr(line, '(');
+    if (!p)
+        return -1;
+    ++p;
+    int open_paren_count = 1;
+    while (*p && open_paren_count > 0)
+    {
+        switch (*p)
+        {
+        case '(':
+            open_paren_count++;
+            break;
+        case ')':
+            open_paren_count--;
+            break;
+        }
+
+        ++p;
+    }
+    if (*p == ' ')
+        ++p;
+    int dummy;
+    int ppid;
+    int count = sscanf(p, "%c %d", (char *) &dummy, &ppid);
+    return count == 2 ? ppid : -1;
+}
+
+} // namespace
+
+void cbGetChildrenPIDs(std::vector<int> &children, int parent)
+{
+#ifndef __WXMSW__
+    const char *c_proc_base = "/proc";
+    DIR *dir = opendir(c_proc_base);
+    if (!dir)
+        return;
+    struct dirent *entry;
+    do
+    {
+        entry = readdir(dir);
+        if (entry)
+        {
+            int pid = atoi(entry->d_name);
+            if (pid != 0)
+            {
+                char filestr[PATH_MAX + 1];
+                snprintf(filestr, PATH_MAX, "%s/%d/stat", c_proc_base, pid);
+                FILE *file = fopen(filestr, "r");
+                if (file)
+                {
+                    char line[101];
+                    fgets(line, 100, file);
+                    fclose(file);
+                    int ppid = ParseParentPID(line);
+                    if (ppid == parent)
+                        children.push_back(pid);
+                }
+            }
+        }
+    } while (entry);
+    closedir(dir);
+#else
+    // get a function pointer to DebugBreakProcess under windows (XP+)
+    typedef HANDLE WINAPI (*CreateToolhelp32SnapshotApiCall)(DWORD dwFlags, DWORD th32ProcessID);
+    static CreateToolhelp32SnapshotApiCall CreateToolhelp32SnapshotFunc = 0;
+    #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+    if (!CreateToolhelp32SnapshotFunc) {
+        kernelLib = LoadLibrary(TEXT("kernel32.dll"));
+        if (kernelLib)
+            CreateToolhelp32SnapshotFunc = (CreateToolhelp32SnapshotApiCall)GetProcAddress(kernelLib, "CreateToolhelp32Snapshot");
+    }
+    #endif
+
+    if ((CreateToolhelp32SnapshotFunc!=NULL) && (Process32FirstFunc!=NULL) && (Process32NextFunc!=NULL) )
+    {
+        HANDLE snap = CreateToolhelp32SnapshotFunc(TH32CS_SNAPALL,0);
+        if (snap!=INVALID_HANDLE_VALUE)
+        {
+            PROCESSENTRY32 lppe;
+            lppe.dwSize = sizeof(PROCESSENTRY32);
+            BOOL ok = Process32FirstFunc(snap, &lppe);
+            while (ok == TRUE)
+            {
+                if (lppe.th32ParentProcessID == parent) // Have my Child...
+                    children.push_back(lppe.th32ProcessID);
+                lppe.dwSize = sizeof(PROCESSENTRY32);
+                ok = Process32NextFunc(snap, &lppe);
+            }
+            CloseHandle(snap);
+        }
+    }
+#endif
+}
--
2.7.0

p.s. This is not the final version of the patch, but it should be good enough to test if the concept is working.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: debugger plugin: catch the inferior PID under gdb
« Reply #22 on: January 30, 2016, 04:56:37 pm »
Ok, I reverted my patch and tried yours: it's working for me. Part of the log:
Quote
Child process PID: 8295

[debug][New Thread 0xb4b27b40 (LWP 8295)]
[debug][New Thread 0xb4b27b40 (LWP 8296)]
[debug][Thread 0xb4b27b40 (LWP 8295) exited]
[debug][New Thread 0xb01ffb40 (LWP 8297)]
[debug]Child process (pid:8295) doesn't exists
[debug]Child PID have not been detected!


Trying to interrupt process with pid: 8290; child pid: 8295 gdb pid: 8288
Continuing...

[debug]Program received signal SIGINT, Interrupt.
[debug]0xb7fdbbe8 in __kernel_vsyscall ()
[debug]>>>>>>cb_gdb:

Btw, your pid detection is triggered only if the wxProcess::Exist() with the old pid fails. I noticed it sometimes does not fail.. ie, I know the pid doesn't exist (there is no such pid in system monitor), but wxProcess::Exist() is still successful. On some other projects it always fails with the old pid, but it's not reliable. So it's probably better to disable the old pid catching (at least on linux).

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #23 on: January 30, 2016, 07:05:38 pm »
I'm fine disabling the pid detection from the log.
In general it needs a threaded program in order to find something, because it looks for the "new thread" messages.
For some reason on your systems the first thread doesn't have the same pid as the process.

Can you check if the parent pid of the threads in your program is the same as the pid of the process?
We can use the old_pid to find the parent pid, but for this to be reliable we'll need to start parsing the "thread exited" messages.
And we should keep a list with all alive threads in the application. This sounds complex and a bit unreliable.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9694
Re: debugger plugin: catch the inferior PID under gdb
« Reply #24 on: January 30, 2016, 09:55:17 pm »
Can you try it? Probably you should disable the pid detection.
Does not compile at all on Windows. Process32FirstFunc and Process32NextFunc are undefined. Did you miss to provide some portions?
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9694
Re: debugger plugin: catch the inferior PID under gdb
« Reply #25 on: January 30, 2016, 10:07:58 pm »
Did you miss to provide some portions?
Maybe the Windows part should more look like this:
Code: cpp
    // get a function pointer to DebugBreakProcess under windows (XP+)
    typedef HANDLE WINAPI CreateToolhelp32SnapshotFunc(DWORD dwFlags, DWORD th32ProcessID);
    static CreateToolhelp32SnapshotFunc* snapshotFunc = NULL;
    typedef BOOL WINAPI Process32FirstFunc(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
    static Process32FirstFunc* firstFunc = NULL;
    typedef BOOL WINAPI Process32NextFunc(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
    static Process32NextFunc* nextFunc = NULL;
    HMODULE kernel32DLL = LoadLibrary(TEXT("kernel32.dll"));
    if (kernel32DLL)
    {
        snapshotFunc = (CreateToolhelp32SnapshotFunc*) GetProcAddress(kernel32DLL, "CreateToolhelp32Snapshot");
        firstFunc    = (Process32FirstFunc*)           GetProcAddress(kernel32DLL, "Process32First");
        nextFunc     = (Process32NextFunc*)            GetProcAddress(kernel32DLL, "Process32Next");
    }
    if ( (NULL!=snapshotFunc) && (NULL!=firstFunc) && (NULL!=nextFunc) )
    {
        HANDLE snap = snapshotFunc(TH32CS_SNAPALL,0);
        if (snap!=INVALID_HANDLE_VALUE)
        {
            PROCESSENTRY32 lppe;
            lppe.dwSize = sizeof(PROCESSENTRY32);
            BOOL ok = firstFunc(snap, &lppe);
            while (ok == TRUE)
            {
                if (lppe.th32ParentProcessID == parent) // Have my Child...
                    children.push_back(lppe.th32ProcessID);
                lppe.dwSize = sizeof(PROCESSENTRY32);
                ok = nextFunc(snap, &lppe);
            }
            CloseHandle(snap);
        }
    }
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #26 on: January 30, 2016, 10:41:04 pm »
I've not tested it on windows, yet.
And I think I have to turn it into a class that is owned by the debugger plugin, so it will be able to do the dll cleanup.

This is just a proof of concept if it solves the problem.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: debugger plugin: catch the inferior PID under gdb
« Reply #27 on: January 31, 2016, 03:12:43 pm »
For some reason on your systems the first thread doesn't have the same pid as the process.
In fact the main thread does have the same pid as the process, but it's not printed by gdb. Gdb only seems to print additionally created threads. As an example, this is a gdb log with thread info (note that there is no "New Thread" message for the main thread):
Code
[New Thread 0xb4b27b40 (LWP 3861)]
[Thread 0xb4b27b40 (LWP 3861) exited]
[New Thread 0xb4b27b40 (LWP 3862)]
[New Thread 0xb01feb40 (LWP 3863)]
^C
Program received signal SIGINT, Interrupt.
0xb7fdbbe8 in __kernel_vsyscall ()
(gdb) info threads
  Id   Target Id         Frame
  4    Thread 0xb01feb40 (LWP 3863) [...]
  3    Thread 0xb4b27b40 (LWP 3862) [...]
* 1    Thread 0xb5240780 (LWP 3857) [...]
(gdb) info program
Using the running image of child Thread 0xb5240780 (LWP 3857).

Quote
Can you check if the parent pid of the threads in your program is the same as the pid of the process?
Yes it is:
Code
~$ ps -L 3857
  PID   LWP TTY      STAT   TIME COMMAND
 3857  3857 pts/9    tl     0:00 [...]
 3857  3862 pts/9    tl     0:00 [...]
 3857  3863 pts/9    tl     0:00 [...]


Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: debugger plugin: catch the inferior PID under gdb
« Reply #28 on: January 31, 2016, 03:41:49 pm »
Thanks. I'll make a proper patch that could be tested.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]