VBoxGDB.cpp
Go to the documentation of this file.
1 // VBoxGDB.cpp : Defines the entry point for the console application.
2 //
3 
4 #include "stdafx.h"
5 #include <stdio.h>
6 #include "../../GDBServerFoundation/GDBServer.h"
7 #include "../../GDBServerFoundation/GDBStub.h"
8 #include "../../GDBServerFoundation/GlobalSessionMonitor.h"
9 #include "VBoxClient.h"
10 #include "VBoxTarget.h"
11 
12 using namespace BazisLib;
13 using namespace GDBServerFoundation;
14 using namespace VBoxGDB;
15 
16 class SimpleStub : public BasicGDBStub
17 {
18  virtual StubResponse HandleRequest(const BazisLib::TempStringA &requestType, char splitterChar, const BazisLib::TempStringA &requestData)
19  {
20  printf("%s%c%s\n", DynamicStringA(requestType).c_str(), splitterChar, DynamicStringA(requestData).c_str());
21  return __super::HandleRequest(requestType, splitterChar, requestData);
22  }
23 
24  virtual void OnProtocolError(const TCHAR *errorDescription)
25  {
26  _tprintf(_T("Protocol error: %s\n"), errorDescription);
27  }
28 };
29 
30 class StubWithLogging : public GDBStub
31 {
32  FILE *pLogFile;
33 
34  virtual StubResponse HandleRequest(const BazisLib::TempStringA &requestType, char splitterChar, const BazisLib::TempStringA &requestData)
35  {
36  printf(">> %s%c%s\n", DynamicStringA(requestType).c_str(), splitterChar, DynamicStringA(requestData).c_str());
37  fprintf(pLogFile, ">> %s%c%s\n", DynamicStringA(requestType).c_str(), splitterChar, DynamicStringA(requestData).c_str());
38  StubResponse response = __super::HandleRequest(requestType, splitterChar, requestData);
39  printf("<< %s\n", std::string(response.GetData(), response.GetSize()).c_str());
40  fprintf(pLogFile, "<< %s\n", std::string(response.GetData(), response.GetSize()).c_str());
41  fflush(pLogFile);
42  return response;
43  }
44 
45 public:
46  StubWithLogging(ISyncGDBTarget *pTarget)
47  : GDBStub(pTarget, true)
48  {
49  pLogFile = fopen("gdbserver.log", "w");
50  }
51 
53  {
54  fclose(pLogFile);
55  }
56 };
57 
58 static volatile LONG s_bStubCreated;
59 
60 class GDBStubFactory : public IGDBStubFactory
61 {
62 private:
63  VBoxClient *m_pClient;
64 
65 public:
67  : m_pClient(pClient)
68  {
69  }
70 
71  virtual IGDBStub *CreateStub(GDBServer *pServer)
72  {
73  if (InterlockedExchange(&s_bStubCreated, 1))
74  return NULL;
75 
76  printf("GDB connected.\n");
77  pServer->StopListening();
78  VBoxTarget32 *pTarget = new VBoxTarget32(m_pClient, false);
79  //return new StubWithLogging(pTarget);
80  return new GDBStub(pTarget);
81  }
82 
83  virtual void OnProtocolError(const TCHAR *errorDescription)
84  {
85  _tprintf(_T("Protocol error: %s\n"), errorDescription);
86  }
87 };
88 
89 #pragma region The code that reproduces the VirtualBox hanging bug
90 static VBoxClient *pClt;
91 
92 BOOL CALLBACK CtrlHandler( DWORD dwType )
93 {
94  if (dwType == CTRL_C_EVENT)
95  {
97  return TRUE;
98  }
99  return FALSE;
100 }
101 
102 bool ReproduceVBoxBug(const char *pHost, int port)
103 {
104  VBoxClient client(BazisLib::Network::DNS::GetHostByName(pHost).SetPort(port));
105  if (!client.Initialize())
106  return false;
107  pClt = &client;
108 
109  client.RequestStopAsync();
110  client.WaitForStop();
111  ULONGLONG val;
112 
113  SetConsoleCtrlHandler(CtrlHandler, TRUE);
114 
115  //The loop will hang around iteration 704 or so. Definitely before 20K.
116  //Commenting/uncommenting random command lines below changes the amount of iterations till hang
117  client.ReadRegister("esp", &val);
118  for (int i = 0;;i++)
119  {
120  client.RequestStopAsync();
121  client.WaitForStop();
122 
123  // client.ReadRegister("esp", &val);
124  char sz[32], szCmd[128];
125  size_t len = 4;
126  sprintf(szCmd, "db %08I64x", val);
127  // if (!client.ReadMemory(val, sz, &len, false))
128  // __asm int 3;
129  client.ExecuteCommand(szCmd);
130  strcpy(szCmd, "showvars");
131  //if (!(i % 5))
132  client.ExecuteCommand(szCmd);
133 
134  printf("%d\n", i);
135  //fflush(stdout);
136  }
137 
138  return true;
139 }
140 #pragma endregion
141 
143 {
144  printf("Usage: VBoxGDB <VirtualBox debug port or host:port> [--gdbport=<GDBServer port>]\n");
145 }
146 
147 
149 
150 static BOOL CALLBACK safeCtrlHandler( DWORD dwType )
151 {
152  if (dwType == CTRL_C_EVENT)
153  {
154  printf("Stopping target... If you intended to close this program, press Ctrl+Break.\n");
155  if (s_pClient)
157  return TRUE;
158  }
159  if (dwType == CTRL_BREAK_EVENT|| dwType == CTRL_CLOSE_EVENT)
160  {
161  if (s_pClient)
163  return FALSE;
164  }
165  return FALSE;
166 }
167 
168 
169 int main(int argc, char* argv[])
170 {
171  printf("VBoxGDB v0.1 [http://sysprogs.com/VBoxGDB]\n");
172  printf("This pre-release version has the following limitations:\n\
173 \t*Only 64-bit VirtualBox 4.1.x is supported\n\
174 \t*Only 32-bit Linux guests are supported\n\
175 \t*Only single-CPU virtual machines are supported\n\
176 ---------------------------------------------------------------------\n");
177 
178  if (argc < 2)
179  {
180  DisplayHelp();
181  return 1;
182  }
183 
184  int vboxPort, gdbPort = 2000;
185  std::string vboxHost = "localhost";
186 
187  char *p = strchr(argv[1], ':');
188  if (!p)
189  vboxPort = atoi(argv[1]);
190  else
191  {
192  vboxHost = std::string(argv[1], p - argv[1]);
193  vboxPort = atoi(p + 1);
194  }
195 
196  for (int i = 2; i < argc; i++)
197  {
198  if (memicmp(argv[i], "--gdbport=", 10))
199  gdbPort = atoi(argv[i] + 10);
200  else if (!strcmp(argv[i], "--tryhang"))
201  ReproduceVBoxBug(vboxHost.c_str(), vboxPort);
202  }
203 
204  printf("Trying to connect to VirtualBox debugger at %s:%d...\n", vboxHost.c_str(), vboxPort);
205  VBoxClient client(BazisLib::Network::DNS::GetHostByName(vboxHost.c_str()).SetPort(vboxPort));
206  if (!client.Initialize())
207  {
208  printf("Cannot connect to VirtualBox debugger.\n");
209  return 1;
210  }
211 
212  s_pClient = &client;
213 
214  printf("Connected to VirtualBox debugger\n", vboxPort);
215 
216  GDBServer srv(new GDBStubFactory(&client));
217  SetConsoleCtrlHandler(safeCtrlHandler, TRUE);
218  printf("Listening for GDB connections on port %d\n", gdbPort);
219  printf("To start debugging, run \"gdb vmlinux\" and execute \"target remote :%d\"\n", gdbPort, gdbPort);
220  srv.Start(gdbPort);
221  srv.WaitForTermination();
222 
223  s_pClient = NULL;
224  return 0;
225 }
226