Developer forums (C::B DEVELOPMENT STRICTLY!) > Development

debugger plugin: catch the inferior PID under gdb

<< < (5/6) > >>

Huki:
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
--- End quote ---

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


--- End code ---

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

oBFusCATed:
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

--- End code ---

p.s. This is not the final version of the patch, but it should be good enough to test if the concept is working.

Huki:
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:

--- End quote ---

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).

oBFusCATed:
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.

MortenMacFly:

--- Quote from: oBFusCATed on January 30, 2016, 12:15:36 am ---Can you try it? Probably you should disable the pid detection.

--- End quote ---
Does not compile at all on Windows. Process32FirstFunc and Process32NextFunc are undefined. Did you miss to provide some portions?

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version