Author Topic: Stoppable gdb for MacOSX - when?  (Read 10554 times)

bnilsson

  • Guest
Stoppable gdb for MacOSX - when?
« on: February 09, 2007, 11:02:09 pm »
When can we expect a stoppable GDB process from within CB for MacOSX?
I will have to put CB aside and go back to CodeWarrior until this is working.
 

Offline afb

  • Developer
  • Lives here!
  • *****
  • Posts: 884
Re: Stoppable gdb for MacOSX - when?
« Reply #1 on: February 10, 2007, 11:20:47 am »
I'm not sure what the timeline of the wxExecute replacement is... ?

If you can come up with a PID workaround for Mac OS X before then,
it might be doable to add that in a __WXMAC__ block in the meantime.
But the default wxMac implementation will return -1 for the process ID,
regardless of what the real number was when invoking the gdb backend.

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2873
Re: Stoppable gdb for MacOSX - when?
« Reply #2 on: February 10, 2007, 03:21:43 pm »
I'm not sure what the timeline of the wxExecute replacement is... ?

If you can come up with a PID workaround for Mac OS X before then,
it might be doable to add that in a __WXMAC__ block in the meantime.
But the default wxMac implementation will return -1 for the process ID,
regardless of what the real number was when invoking the gdb backend.


Something like the following might work. I issue a "ps x" command and capture the output, then scan the output for the program/pid of interest.
In this particular code, I'm looking for the tty (second ps output field) of the sleep command which was started by a "xterm -e sleep 80000" command.
Code
+// ----------------------------------------------------------------------------
+wxString GDB_driver::GetConsoleTty(int ConsolePid)
+// ----------------------------------------------------------------------------
+{
+    // execute the ps -x command  and read PS output to get the /dev/tty field
+
+ unsigned long ConsPid = ConsolePid;
+ wxString psCmd;
+ wxArrayString psOutput;
+ wxArrayString psErrors;
+
+ psCmd << wxT("ps x");
+    m_pDBG->Log(wxString::Format( _("Executing: %s"), psCmd.c_str()) );
+ int result = wxExecute(psCmd, psOutput, psErrors, wxEXEC_SYNC);
+ psCmd.Clear();
+ if (result != 0)
+ {   psCmd << wxT("Result of ps x:") << result;
+        m_pDBG->Log(wxString::Format( _("Execution Error:"), psCmd.c_str()) );
+        return wxEmptyString;
+ }
+
+    wxString ConsTtyStr;
+    wxString ConsPidStr;
+    ConsPidStr << ConsPid;
+    //find task with our unique sleep time
+    wxString uniqueSleepTimeStr;
+    uniqueSleepTimeStr << wxT("sleep ") << 80000 + ::wxGetProcessId();
+    // search the output of "ps pid" command
+    int knt = psOutput.GetCount();
+    for (int i=knt-1; i>-1; --i)
+    {   psCmd = psOutput.Item(i);
+        m_pDBG->Log(wxString::Format( _("PS result: %s"), psCmd.c_str()) );
+        // find the pts/# or tty/# or whatever it's called
+        // by seaching the output for our pid in character
+        // The output of ps looks like:
+        //   PID TTY      STAT   TIME COMMAND
+        // 8779 pts/3    Ss+    0:00 sleep 600000
+        //if (psCmd.Contains(ConsPidStr))
+        if (psCmd.Contains(uniqueSleepTimeStr))
+        {   ConsTtyStr = psCmd.Mid( ConsPidStr.Length()+2);
+            ConsTtyStr = wxT("/dev/")+ConsTtyStr.BeforeFirst(' ');
+            m_pDBG->Log(wxString::Format( _("TTY is[%s]"), ConsTtyStr.c_str()) );
+            return ConsTtyStr;
+        }//if
+    }//for
+
+    knt = psErrors.GetCount();
+    for (int i=0; i<knt; ++i)
+        m_pDBG->Log(wxString::Format( _("PS Error:%s"), psErrors.Item(i).c_str()) );
+    return wxEmptyString;
+}


bnilsson

  • Guest
Re: Stoppable gdb for MacOSX - when?
« Reply #3 on: February 18, 2007, 03:04:20 pm »
I wonder...

After a debug session ends, I find the following process with 'ps x':

16989  ??  Z      0:00.00 (gdb-powerpc-appl)

Is this a candidate for the process to be stopped?
If I quit CB it disappears, and it does not appear again until CB is restarted and a new debug session is started and terminated.
I cannot kill it by 'kill -9 16989', nothing happens.
So I guess trying to do the same from within the program would not work either.
Can it be stopped by any other method?

« Last Edit: February 18, 2007, 09:34:30 pm by bnilsson »

bnilsson

  • Guest
Re: Stoppable gdb for MacOSX - when?
« Reply #4 on: February 18, 2007, 10:03:03 pm »
Some more:

When waiting at a breakpoint, 'ps x' shows

18159  ??  S      0:02.62 /usr/libexec/gdb/gdb-powerpc-apple-darwin -nx -fullname -quiet -args bin/Debug/wxProject.app/Contents/MacOS/wxProject

After the debugging is terminated, either by Continue or Stop Debugger, 'ps x' shows

18159  ??  Z      0:00.00 (gdb-powerpc-appl)

apparently a zombie process that I cannot get rid of by command line.

If I do 'kill -9' on the process before it has become a zombie (standing at the breakpoint), it immidiately becomes one (a zombie).
Continuing the debugging after that crashes CB altogether, which is not strange.

Any suggestions would be appreciated.

 

Offline afb

  • Developer
  • Lives here!
  • *****
  • Posts: 884
Re: Stoppable gdb for MacOSX - when?
« Reply #5 on: March 02, 2007, 10:53:48 am »
Since it seems 1.0 didn't happen, I started building for Mac OS X again. (10.4 only at the moment)

Released rev 3656 to BerliOS, but doesn't included patches for Terminal.app or any new for GDB...
I'll see if I can integrate the xterm -> Terminal.app, and maybe something like Pecan's "ps" code.

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2873
Re: Stoppable gdb for MacOSX - when?
« Reply #6 on: March 02, 2007, 03:32:08 pm »
Since it seems 1.0 didn't happen, I started building for Mac OS X again. (10.4 only at the moment)

Released rev 3656 to BerliOS, but doesn't included patches for Terminal.app or any new for GDB...
I'll see if I can integrate the xterm -> Terminal.app, and maybe something like Pecan's "ps" code.

Have a look at the following:
1) provides a xterm/terminal for the debuggee
2) fixes crashes during gdb termination
3) allows user to stop debuggee in I/O or loop. User has to hit the stop button twice. Once to stop gdb, then again to kill the debuggee.

I haven't yet figured out a fix for crashes when deleting a breakpoint.

Code
Index: debuggerdriver.cpp
===================================================================
--- debuggerdriver.cpp (revision 3654)
+++ debuggerdriver.cpp (working copy)
@@ -5,6 +5,7 @@
 DebuggerDriver::DebuggerDriver(DebuggerGDB* plugin)
     : m_pDBG(plugin),
     m_ProgramIsStopped(true),
+    m_IsStarted(false), //(pecan 2007/3/01)
     m_ChildPID(0),
     m_pBacktrace(0),
     m_pDisassembly(0),
Index: debuggergdb.cpp
===================================================================
--- debuggergdb.cpp (revision 3654)
+++ debuggergdb.cpp (working copy)
@@ -227,12 +227,18 @@
     m_pBreakpointsWindow(0),
     m_pExamineMemoryDlg(0),
     m_pThreadsDlg(0),
-    m_pProject(0)
+    m_pProject(0),
+    m_GDBInputKnt(0)
 {
     if(!Manager::LoadResource(_T("debugger.zip")))
     {
         NotifyMissingFile(_T("debugger.zip"));
     }
+    // vars for Linux console //(pecan 2007/2/06)
+    m_bIsConsole = false;
+    m_nConsolePid = 0;
+    m_ConsoleTty = wxEmptyString;
+
 }
 
 DebuggerGDB::~DebuggerGDB()
@@ -789,7 +795,11 @@
 {
     return !m_State.HasDriver() || m_State.GetDriver()->IsStopped();
 }
-
+bool DebuggerGDB::IsStarted() //(pecan 2007/3/01)
+{
+    if (!m_State.HasDriver()) return false;
+    return m_State.GetDriver()->IsStarted();
+}
 int DebuggerGDB::Debug()
 {
     // if already running, return
@@ -1021,6 +1031,21 @@
     m_State.GetDriver()->Prepare(target && target->GetTargetType() == ttConsoleOnly);
     m_State.ApplyBreakpoints();
 
+   #ifdef __WXGTK__    //(pecan 2007/2/05)
+    // create xterm and issue tty "/dev/pts/#" to GDB where
+    // # is the tty for the newly created xterm
+    m_bIsConsole = (target && target->GetTargetType() == ttConsoleOnly);
+    if (m_bIsConsole)
+    {
+        if (RunNixConsole() > 0 )
+        {   wxString gdbTtyCmd;
+            gdbTtyCmd << wxT("tty ") << m_ConsoleTty;
+            m_State.GetDriver()->QueueCommand(new DebuggerCmd(m_State.GetDriver(), gdbTtyCmd, true));
+            DebugLog(wxString::Format( _("Queued:[%s]"), gdbTtyCmd.c_str()) );
+        }
+    }//if
+   #endif//def __WXGTK__
+
     // Don't issue 'run' if attaching to a process (Bug #1391904)
     if (m_PidToAttach == 0)
         m_State.GetDriver()->Start(m_BreakOnEntry);
@@ -1503,12 +1528,24 @@
 
 void DebuggerGDB::Stop()
 {
+    // m_Process is PipedProcess I/O; m_Pid is debugger pid
     if (m_pProcess && m_Pid)
     {
-        if (IsStopped())
+        //-if (IsStopped()) //no Driver || m_IsStopped
+        //-if (IsStopped() && IsStarted() ) //(pecan 2007/3/01)
+        DebugLog(wxString::Format(wxT("IsStopped:[%d] IsStarted:[%d]"),IsStopped(),IsStarted()));
+        if (IsStarted() ) //(pecan 2007/3/01)
         {
             RunCommand(CMD_STOP);
             m_pProcess->CloseOutput();
+           #ifdef __WXGTK__
+            // kill any linux console //(pecan 2007/2/06)
+            if ( m_bIsConsole && (m_nConsolePid > 0) )
+            {
+                ::wxKill(m_nConsolePid);
+                m_nConsolePid = 0;
+            }
+           #endif
         }
         else
         {
@@ -1521,7 +1558,8 @@
                     _("Debug"), wxOK | wxICON_EXCLAMATION);
             else
         #endif
-            wxKill(pid, wxSIGINT);
+            if (pid > 0)
+                wxKill(pid, wxSIGINT);
         #else
             m_pProcess->CloseOutput();
             wxKillError err = m_pProcess->Kill(m_Pid, wxSIGKILL);
@@ -1909,12 +1947,19 @@
 
 void DebuggerGDB::OnGDBOutput(wxCommandEvent& event)
 {
+    ++m_GDBInputKnt;
     wxString msg = event.GetString();
     if (!msg.IsEmpty())
     {
 //        Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("O>>> %s"), msg.c_str());
         ParseOutput(msg);
     }
+    --m_GDBInputKnt;
+    if ( (not m_pProcess) && m_State.HasDriver() && (m_GDBInputKnt==0) )
+    {   m_State.StopDriver();
+        DebugLog(wxT("OnGDBOutput() closed m_State.Driver"));
+    }
+
 }
 
 void DebuggerGDB::OnGDBError(wxCommandEvent& event)
@@ -1937,7 +1982,14 @@
 //    m_pProcess = 0L;
 
     ClearActiveMarkFromAllEditors();
-    m_State.StopDriver();
+    // closing the GDB driver here causes crashes because input msgs
+    // are still queued up in OnGDBOutput/ParseOutput //(pecan 2007/2/09)
+    //-m_State.StopDriver();
+    if ( (not m_pProcess) && m_State.HasDriver() && (m_GDBInputKnt==0) )
+    {   m_State.StopDriver();
+        DebugLog(wxT("OnGDBTerminate() closed m_State.Driver"));
+    }
+
     Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Debugger finished with status %d"), m_LastExitCode);
 
     if (m_NoDebugInfo)
@@ -2198,3 +2250,95 @@
 {
     Configure();
 }
+// ----------------------------------------------------------------------------
+int DebuggerGDB::RunNixConsole()
+// ----------------------------------------------------------------------------
+{
+    // start the xterm and put the shell to sleep with -e sleep 80000
+    // fetch the xterm tty so we can issue to gdb a "tty /dev/pts/#"
+    // redirecting program stdin/stdout/stderr to the xterm console.
+
+  #ifndef __WXMSW__
+    wxString cmd;
+    wxString title = wxT("Program Console");
+    m_nConsolePid = 0;
+    // for non-win platforms, use m_ConsoleTerm to run the console app
+    wxString term = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_terminal"), DEFAULT_CONSOLE_TERM);
+    //term.Replace(_T("$TITLE"), _T("'") + _T("*nixConsole") + _T("'"));
+    term.Replace(_T("$TITLE"), _T("'") + title + _T("'"));
+    cmd << term << _T(" ");
+    cmd << wxT("sleep ");
+    cmd << 80000 + ::wxGetProcessId(); //make a unique sleep command
+
+    Manager::Get()->GetMacrosManager()->ReplaceEnvVars(cmd);
+    //Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Executing: %s"), cmd.c_str() );
+    DebugLog(wxString::Format( _("Executing: %s"), cmd.c_str()) );
+    //start xterm -e sleep {some unique # of seconds}
+    m_nConsolePid = wxExecute(cmd, wxEXEC_ASYNC);
+    if (m_nConsolePid <= 0) return -1;
+
+    // Issue the PS command with to get the /dev/tty device name
+    // First, wait for the xterm to settle down, else PS won't see the sleep task
+    Manager::Yield();
+    ::wxSleep(1);
+    m_ConsoleTty = GetConsoleTty(m_nConsolePid);
+    if (not m_ConsoleTty.IsEmpty() )
+        return m_nConsolePid;
+    // failed to find the console tty
+    DebugLog( wxT("Console Execution error:failed to find console tty."));
+    ::wxKill(m_nConsolePid);
+    m_nConsolePid = 0;
+  #endif//ndef __WWXMSW__
+    return -1;
+}
+// ----------------------------------------------------------------------------
+wxString DebuggerGDB::GetConsoleTty(int ConsolePid)
+// ----------------------------------------------------------------------------
+{
+    // execute the ps -x command  and read PS output to get the /dev/tty field
+
+ unsigned long ConsPid = ConsolePid;
+ wxString psCmd;
+ wxArrayString psOutput;
+ wxArrayString psErrors;
+
+ psCmd << wxT("ps x");
+    DebugLog(wxString::Format( _("Executing: %s"), psCmd.c_str()) );
+ int result = wxExecute(psCmd, psOutput, psErrors, wxEXEC_SYNC);
+ psCmd.Clear();
+ if (result != 0)
+ {   psCmd << wxT("Result of ps x:") << result;
+        DebugLog(wxString::Format( _("Execution Error:"), psCmd.c_str()) );
+        return wxEmptyString;
+ }
+
+    wxString ConsTtyStr;
+    wxString ConsPidStr;
+    ConsPidStr << ConsPid;
+    //find task with our unique sleep time
+    wxString uniqueSleepTimeStr;
+    uniqueSleepTimeStr << wxT("sleep ") << 80000 + ::wxGetProcessId();
+    // search the output of "ps pid" command
+    int knt = psOutput.GetCount();
+    for (int i=knt-1; i>-1; --i)
+    {   psCmd = psOutput.Item(i);
+        DebugLog(wxString::Format( _("PS result: %s"), psCmd.c_str()) );
+        // find the pts/# or tty/# or whatever it's called
+        // by seaching the output or out "ps x" command.
+        // The output of ps looks like:
+        //   PID TTY      STAT   TIME COMMAND
+        // 8779 pts/3    Ss+    0:00 sleep 600000
+        //if (psCmd.Contains(ConsPidStr))
+        if (psCmd.Contains(uniqueSleepTimeStr))
+        {   ConsTtyStr = psCmd.Mid( ConsPidStr.Length()+2);
+            ConsTtyStr = wxT("/dev/")+ConsTtyStr.BeforeFirst(' ');
+            DebugLog(wxString::Format( _("TTY is[%s]"), ConsTtyStr.c_str()) );
+            return ConsTtyStr;
+        }//if
+    }//for
+
+    knt = psErrors.GetCount();
+    for (int i=0; i<knt; ++i)
+        DebugLog(wxString::Format( _("PS Error:%s"), psErrors.Item(i).c_str()) );
+    return wxEmptyString;
+}
Index: gdb_driver.h
===================================================================
--- gdb_driver.h (revision 3654)
+++ gdb_driver.h (working copy)
@@ -86,24 +86,24 @@
 
         // Seems to be intended to allow step before program has started.
         // Was always false.  HC changed to take value from DebuggerGDB::m_BreakOnEntry.
-        bool m_BreakOnEntry;
-   
+        bool m_BreakOnEntry;
+
         // Seems to be used to issue a InfoProgram command, then continue
         // True after first "Start()", until first break
-        bool m_ManualBreakOnEntry;
+        bool m_ManualBreakOnEntry;
 
- // Program is "running": after a "run" or a "start", and before "kill" or a "quit"
- bool m_IsStarted;
-
+// // Program is "running": after a "run" or a "start", and before "kill" or a "quit"
+// bool m_IsStarted;
+
         // cursor update flags
         bool m_needsUpdate;
         bool m_forceUpdate;
-
+
         // GDB version
         long m_GDBVersionMajor;
         long m_GDBVersionMinor;
         wxString flavour;
-
+
 }; // GDB_driver
 
 #endif // GDB_DRIVER_H
Index: debuggerdriver.h
===================================================================
--- debuggerdriver.h (revision 3654)
+++ debuggerdriver.h (working copy)
@@ -132,6 +132,7 @@
 
         /** Is the program stopped? */
         virtual bool IsStopped(){ return m_ProgramIsStopped; }
+        virtual bool IsStarted(){ return m_IsStarted; } //(pecan 2007/3/01)
         /** Get debugger cursor. */
         virtual const Cursor& GetCursor() const { return m_Cursor; }
         /** Set child PID (debuggee's). Usually set by debugger commands. */
@@ -162,6 +163,11 @@
 
         // cursor related
         bool m_ProgramIsStopped;
+        //(pecan 2007/3/01)
+        // moved here from gdb_driver.h
+ // Program is "running": after a "run" or a "start", and before "kill" or a "quit"
+ bool m_IsStarted;
+        ;
         wxString m_LastCursorAddress;
         Cursor m_Cursor;
 
Index: debuggergdb.h
===================================================================
--- debuggergdb.h (revision 3654)
+++ debuggergdb.h (working copy)
@@ -110,6 +110,7 @@
         int LaunchProcess(const wxString& cmd, const wxString& cwd);
         wxString GetDebuggee(ProjectBuildTarget* target);
         bool IsStopped();
+        bool IsStarted(); //(pecan 2007/3/01)
 
         void OnUpdateUI(wxUpdateUIEvent& event);
         void OnDebug(wxCommandEvent& event);
@@ -204,7 +205,15 @@
         SearchDirsMap m_SearchDirs;
 
         int m_HookId; // project loader hook ID
+        int m_GDBInputKnt;
 
+        // Linux console support
+        int      RunNixConsole();
+        wxString GetConsoleTty(int ConsolePid);
+        bool     m_bIsConsole;
+        int      m_nConsolePid;
+        wxString m_ConsoleTty;
+
  DECLARE_EVENT_TABLE()
 };
 
Index: gdb_driver.cpp
===================================================================
--- gdb_driver.cpp (revision 3654)
+++ gdb_driver.cpp (working copy)
@@ -55,7 +55,7 @@
     : DebuggerDriver(plugin),
     m_BreakOnEntry(false),
     m_ManualBreakOnEntry(false),
- m_IsStarted(false),
+// m_IsStarted(false), //(pecan 2007/3/01)
     m_GDBVersionMajor(0),
     m_GDBVersionMinor(0)
 {

« Last Edit: March 02, 2007, 03:39:32 pm by Pecan »

Offline afb

  • Developer
  • Lives here!
  • *****
  • Posts: 884
Re: Stoppable gdb for MacOSX - when?
« Reply #7 on: March 02, 2007, 03:44:23 pm »
Thanks, maybe parts of it can be adapted...

I'm using: /bin/ps -o ppid,pid,command
« Last Edit: March 02, 2007, 04:27:38 pm by afb »

Offline afb

  • Developer
  • Lives here!
  • *****
  • Posts: 884
Re: Stoppable gdb for MacOSX - when?
« Reply #8 on: March 02, 2007, 04:42:35 pm »

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2873
Re: Stoppable gdb for MacOSX - when?
« Reply #9 on: March 02, 2007, 05:19:30 pm »

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2873
Re: Stoppable gdb for MacOSX - when?
« Reply #10 on: March 02, 2007, 07:04:37 pm »
Here is a first hack: http://www.algonet.se/~afb/wx/codeblocks-macgdbpid.patch

 "if (psLine.StartsWith(mypidStr) " may not work.
I've noticed that when the output has longer and shorter length pids,
some pids may have a variable number of blanks before the pid.

And thanks for the "ps -o" idea. I've found that "ps x -o" is safer though. It appears that, on ubuntu at least, some output is skipped on "ps -o" and I miss finding the correct TTY for the console.



« Last Edit: March 02, 2007, 07:10:28 pm by Pecan »

Offline afb

  • Developer
  • Lives here!
  • *****
  • Posts: 884
Re: Stoppable gdb for MacOSX - when?
« Reply #11 on: March 03, 2007, 12:05:08 am »
"if (psLine.StartsWith(mypidStr) " may not work.
I've noticed that when the output has longer and shorter length pids,
some pids may have a variable number of blanks before the pid.

You are right, it does - needs to be trimmed first, thanks!

Edit: patch updated
« Last Edit: March 03, 2007, 01:32:27 am by afb »