2005-07-29 13:46:03 +00:00
|
|
|
/* $Id$
|
2001-04-15 23:43:12 +00:00
|
|
|
*
|
|
|
|
* gdb2 - gdb output splitter
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000,2001 Nedko Arnaoudov <nedkohome@atia.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ph.h"
|
|
|
|
|
|
|
|
#define GDB_INITIAL_COMMAND "gdb.exe"
|
|
|
|
#define PARAMS_SEPARATOR " "
|
|
|
|
#define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR);
|
|
|
|
|
|
|
|
void DisplayError(char *pszAPI);
|
|
|
|
void ReadAndHandleOutput(HANDLE hPipeRead);
|
|
|
|
void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
|
|
|
|
HANDLE hChildStdIn,
|
|
|
|
HANDLE hChildStdErr,
|
|
|
|
char *pchCommandLine);
|
|
|
|
|
|
|
|
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
|
|
|
|
HANDLE hChildProcess = NULL;
|
|
|
|
HANDLE hStdIn = NULL; // Handle to parents std input.
|
|
|
|
BOOL bRunThread = TRUE;
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
|
|
|
|
HANDLE hInputWriteTmp,hInputRead,hInputWrite;
|
|
|
|
HANDLE hErrorWrite;
|
|
|
|
HANDLE hThread;
|
|
|
|
DWORD ThreadId;
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
|
|
|
|
// Set up the security attributes struct.
|
|
|
|
sa.nLength= sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
sa.lpSecurityDescriptor = NULL;
|
|
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
// Create the child output pipe.
|
|
|
|
if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
|
|
|
|
DisplayError("CreatePipe");
|
|
|
|
|
|
|
|
|
|
|
|
// Create a duplicate of the output write handle for the std error
|
|
|
|
// write handle. This is necessary in case the child application
|
|
|
|
// closes one of its std output handles.
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
|
|
|
|
GetCurrentProcess(),&hErrorWrite,0,
|
|
|
|
TRUE,DUPLICATE_SAME_ACCESS))
|
|
|
|
DisplayError("DuplicateHandle");
|
|
|
|
|
|
|
|
|
|
|
|
// Create the child input pipe.
|
|
|
|
if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
|
|
|
|
DisplayError("CreatePipe");
|
|
|
|
|
|
|
|
|
|
|
|
// Create new output read handle and the input write handles. Set
|
|
|
|
// the Properties to FALSE. Otherwise, the child inherits the
|
|
|
|
// properties and, as a result, non-closeable handles to the pipes
|
|
|
|
// are created.
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
|
|
|
|
GetCurrentProcess(),
|
|
|
|
&hOutputRead, // Address of new handle.
|
|
|
|
0,FALSE, // Make it uninheritable.
|
|
|
|
DUPLICATE_SAME_ACCESS))
|
|
|
|
DisplayError("DupliateHandle");
|
|
|
|
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
|
|
|
|
GetCurrentProcess(),
|
|
|
|
&hInputWrite, // Address of new handle.
|
|
|
|
0,FALSE, // Make it uninheritable.
|
|
|
|
DUPLICATE_SAME_ACCESS))
|
|
|
|
DisplayError("DupliateHandle");
|
|
|
|
|
|
|
|
|
|
|
|
// Close inheritable copies of the handles you do not want to be
|
|
|
|
// inherited.
|
|
|
|
if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
|
|
|
|
if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");
|
|
|
|
|
|
|
|
|
|
|
|
// Get std input handle so you can close it and force the ReadFile to
|
|
|
|
// fail when you want the input thread to exit.
|
|
|
|
if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
|
|
|
|
INVALID_HANDLE_VALUE )
|
|
|
|
DisplayError("GetStdHandle");
|
|
|
|
|
|
|
|
SetConsoleTitle("gdb control console");
|
|
|
|
|
|
|
|
size_t size = strlen(GDB_INITIAL_COMMAND)+PARAMS_SEPARATOR_LEN;
|
|
|
|
|
|
|
|
// get parameters length
|
|
|
|
for (int i = 1 ; i < argc ; i++)
|
|
|
|
size += strlen(argv[i])+PARAMS_SEPARATOR_LEN;
|
|
|
|
|
|
|
|
size++; // terminating null
|
|
|
|
|
|
|
|
char *pszCommandLine = new char [size];
|
|
|
|
if (!pszCommandLine)
|
|
|
|
{
|
|
|
|
printf("out of memory.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(pszCommandLine,GDB_INITIAL_COMMAND);
|
|
|
|
for (int i = 1 ; i < argc ; i++)
|
|
|
|
{
|
|
|
|
strcat(pszCommandLine,PARAMS_SEPARATOR);
|
|
|
|
strcat(pszCommandLine,argv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite,pszCommandLine);
|
|
|
|
|
|
|
|
|
|
|
|
// Close pipe handles (do not continue to modify the parent).
|
|
|
|
// You need to make sure that no handles to the write end of the
|
|
|
|
// output pipe are maintained in this process or else the pipe will
|
|
|
|
// not close when the child process exits and the ReadFile will hang.
|
|
|
|
if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
|
|
|
|
if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
|
|
|
|
if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");
|
|
|
|
|
|
|
|
|
|
|
|
// Launch the thread that gets the input and sends it to the child.
|
|
|
|
hThread = CreateThread(NULL,0,GetAndSendInputThread,
|
|
|
|
(LPVOID)hInputWrite,0,&ThreadId);
|
|
|
|
if (hThread == NULL) DisplayError("CreateThread");
|
|
|
|
|
|
|
|
|
|
|
|
// Read the child's output.
|
|
|
|
ReadAndHandleOutput(hOutputRead);
|
|
|
|
// Redirection is complete
|
|
|
|
|
|
|
|
|
|
|
|
// Force the read on the input to return by closing the stdin handle.
|
|
|
|
if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");
|
|
|
|
|
|
|
|
|
|
|
|
// Tell the thread to exit and wait for thread to die.
|
|
|
|
bRunThread = FALSE;
|
|
|
|
|
|
|
|
if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
|
|
|
|
DisplayError("WaitForSingleObject");
|
|
|
|
|
|
|
|
if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
|
|
|
|
if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// PrepAndLaunchRedirectedChild
|
|
|
|
// Sets up STARTUPINFO structure, and launches redirected child.
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
|
|
|
|
HANDLE hChildStdIn,
|
|
|
|
HANDLE hChildStdErr,
|
|
|
|
char *pchCommandLine)
|
|
|
|
{
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
STARTUPINFO si;
|
|
|
|
|
|
|
|
// Set up the start up info struct.
|
|
|
|
ZeroMemory(&si,sizeof(STARTUPINFO));
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
si.hStdOutput = hChildStdOut;
|
|
|
|
si.hStdInput = hChildStdIn;
|
|
|
|
si.hStdError = hChildStdErr;
|
|
|
|
si.lpTitle = "debugged program console";
|
|
|
|
// Use this if you want to hide the child:
|
|
|
|
// si.wShowWindow = SW_HIDE;
|
|
|
|
// Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
|
|
|
|
// use the wShowWindow flags.
|
|
|
|
|
|
|
|
|
|
|
|
// Launch the process that you want to redirect (in this case,
|
|
|
|
// Child.exe). Make sure Child.exe is in the same directory as
|
|
|
|
// redirect.c launch redirect from a command line to prevent location
|
|
|
|
// confusion.
|
|
|
|
if (!CreateProcess(NULL,pchCommandLine,NULL,NULL,TRUE,
|
|
|
|
CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
|
|
|
|
DisplayError("CreateProcess");
|
|
|
|
|
|
|
|
|
|
|
|
// Set global child process handle to cause threads to exit.
|
|
|
|
hChildProcess = pi.hProcess;
|
|
|
|
|
|
|
|
|
|
|
|
// Close any unnecessary handles.
|
|
|
|
if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// ReadAndHandleOutput
|
|
|
|
// Monitors handle for input. Exits when child exits or pipe breaks.
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
void ReadAndHandleOutput(HANDLE hPipeRead)
|
|
|
|
{
|
|
|
|
CHAR lpBuffer[256];
|
|
|
|
DWORD nBytesRead;
|
|
|
|
DWORD nCharsWritten;
|
|
|
|
|
|
|
|
while(TRUE)
|
|
|
|
{
|
|
|
|
if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
|
|
|
|
&nBytesRead,NULL) || !nBytesRead)
|
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
|
|
|
break; // pipe done - normal exit path.
|
|
|
|
else
|
|
|
|
DisplayError("ReadFile"); // Something bad happened.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display the character read on the screen.
|
|
|
|
if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
|
|
|
|
nBytesRead,&nCharsWritten,NULL))
|
|
|
|
DisplayError("WriteConsole");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// GetAndSendInputThread
|
|
|
|
// Thread procedure that monitors the console for input and sends input
|
|
|
|
// to the child process through the input pipe.
|
|
|
|
// This thread ends when the child application exits.
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
|
|
|
|
{
|
|
|
|
CHAR read_buff[256];
|
|
|
|
DWORD nBytesRead,nBytesWrote;
|
|
|
|
HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
|
|
|
|
|
|
|
|
// Get input from our console and send it to child through the pipe.
|
|
|
|
while (bRunThread)
|
|
|
|
{
|
|
|
|
if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
|
|
|
|
DisplayError("ReadConsole");
|
|
|
|
|
|
|
|
read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
|
|
|
|
|
|
|
|
if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
|
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_NO_DATA)
|
|
|
|
break; // Pipe was closed (normal exit path).
|
|
|
|
else
|
|
|
|
DisplayError("WriteFile");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// DisplayError
|
|
|
|
// Displays the error number and corresponding message.
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
void DisplayError(char *pszAPI)
|
|
|
|
{
|
|
|
|
LPVOID lpvMessageBuffer;
|
|
|
|
CHAR szPrintBuffer[512];
|
|
|
|
DWORD nCharsWritten;
|
|
|
|
|
|
|
|
FormatMessage(
|
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
|
|
|
|
NULL, GetLastError(),
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(LPTSTR)&lpvMessageBuffer, 0, NULL);
|
|
|
|
|
|
|
|
wsprintf(szPrintBuffer,
|
|
|
|
"ERROR: API = %s.\n error code = %d.\n message = %s.\n",
|
|
|
|
pszAPI, GetLastError(), (char *)lpvMessageBuffer);
|
|
|
|
|
|
|
|
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
|
|
|
|
lstrlen(szPrintBuffer),&nCharsWritten,NULL);
|
|
|
|
|
|
|
|
LocalFree(lpvMessageBuffer);
|
|
|
|
ExitProcess(GetLastError());
|
|
|
|
}
|