VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCTcp.cpp@ 85972

Last change on this file since 85972 was 84627, checked in by vboxsync, 5 years ago

Debugger: Initial port of my GDB stub library from https://github.com/AlexanderEichner/libgdbstub to VirtualBox, bugref:5217

Very basic port with quite a few rough edges, this is basically a backup of the current code.

Working:

  • Halt and Resume VM execution
  • Reading registers
  • Reading/Writing memory
  • Basic support for breakpoints (removing doesn't work yet)

Still todo:

  • Cleanup, cleanup, cleanup
  • Writing memory
  • SMP support (by abusing the thread support of GDB)
  • Access to the native debugger console through the Rcmd/monitor protocol
  • More register sets (MMX,SSE,AVX, etc.)

If you want to try it out enable the normal TCP debugger interface and set the following extradata key:

VBoxInternal/DBGC/GdbStub 1

No complaints about the code at this point please, hints and comments welcome though!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.8 KB
Line 
1/* $Id: DBGCTcp.cpp 84627 2020-06-01 20:13:57Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, TCP backend.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <VBox/dbg.h>
23#include <VBox/vmm/cfgm.h>
24#include <VBox/err.h>
25
26#include <iprt/thread.h>
27#include <iprt/tcp.h>
28#include <VBox/log.h>
29#include <iprt/assert.h>
30
31#include <iprt/string.h>
32
33#include "DBGCInternal.h"
34
35
36/*********************************************************************************************************************************
37* Structures and Typedefs *
38*********************************************************************************************************************************/
39/**
40 * Debug console TCP backend instance data.
41 */
42typedef struct DBGCTCP
43{
44 /** The I/O backend for the console. */
45 DBGCBACK Back;
46 /** The socket of the connection. */
47 RTSOCKET Sock;
48 /** Connection status. */
49 bool fAlive;
50} DBGCTCP;
51/** Pointer to the instance data of the console TCP backend. */
52typedef DBGCTCP *PDBGCTCP;
53
54/** Converts a pointer to DBGCTCP::Back to a pointer to DBGCTCP. */
55#define DBGCTCP_BACK2DBGCTCP(pBack) ( (PDBGCTCP)((char *)(pBack) - RT_UOFFSETOF(DBGCTCP, Back)) )
56
57
58/*********************************************************************************************************************************
59* Internal Functions *
60*********************************************************************************************************************************/
61static DECLCALLBACK(int) dbgcTcpConnection(RTSOCKET Sock, void *pvUser);
62
63
64
65/**
66 * Checks if there is input.
67 *
68 * @returns true if there is input ready.
69 * @returns false if there not input ready.
70 * @param pBack Pointer to the backend structure supplied by
71 * the backend. The backend can use this to find
72 * it's instance data.
73 * @param cMillies Number of milliseconds to wait on input data.
74 */
75static DECLCALLBACK(bool) dbgcTcpBackInput(PDBGCBACK pBack, uint32_t cMillies)
76{
77 PDBGCTCP pDbgcTcp = DBGCTCP_BACK2DBGCTCP(pBack);
78 if (!pDbgcTcp->fAlive)
79 return false;
80 int rc = RTTcpSelectOne(pDbgcTcp->Sock, cMillies);
81 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
82 pDbgcTcp->fAlive = false;
83 return rc != VERR_TIMEOUT;
84}
85
86
87/**
88 * Read input.
89 *
90 * @returns VBox status code.
91 * @param pBack Pointer to the backend structure supplied by
92 * the backend. The backend can use this to find
93 * it's instance data.
94 * @param pvBuf Where to put the bytes we read.
95 * @param cbBuf Maximum nymber of bytes to read.
96 * @param pcbRead Where to store the number of bytes actually read.
97 * If NULL the entire buffer must be filled for a
98 * successful return.
99 */
100static DECLCALLBACK(int) dbgcTcpBackRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
101{
102 PDBGCTCP pDbgcTcp = DBGCTCP_BACK2DBGCTCP(pBack);
103 if (!pDbgcTcp->fAlive)
104 return VERR_INVALID_HANDLE;
105 int rc = RTTcpRead(pDbgcTcp->Sock, pvBuf, cbBuf, pcbRead);
106 if (RT_SUCCESS(rc) && pcbRead != NULL && *pcbRead == 0)
107 rc = VERR_NET_SHUTDOWN;
108 if (RT_FAILURE(rc))
109 pDbgcTcp->fAlive = false;
110 return rc;
111}
112
113/**
114 * Write (output).
115 *
116 * @returns VBox status code.
117 * @param pBack Pointer to the backend structure supplied by
118 * the backend. The backend can use this to find
119 * it's instance data.
120 * @param pvBuf What to write.
121 * @param cbBuf Number of bytes to write.
122 * @param pcbWritten Where to store the number of bytes actually written.
123 * If NULL the entire buffer must be successfully written.
124 */
125static DECLCALLBACK(int) dbgcTcpBackWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
126{
127 PDBGCTCP pDbgcTcp = DBGCTCP_BACK2DBGCTCP(pBack);
128 if (!pDbgcTcp->fAlive)
129 return VERR_INVALID_HANDLE;
130
131 /*
132 * convert '\n' to '\r\n' while writing.
133 */
134 int rc = 0;
135 size_t cbLeft = cbBuf;
136 while (cbLeft)
137 {
138 size_t cb = cbLeft;
139 /* write newlines */
140 if (*(const char *)pvBuf == '\n')
141 {
142 rc = RTTcpWrite(pDbgcTcp->Sock, "\r\n", 2);
143 cb = 1;
144 }
145 /* write till next newline */
146 else
147 {
148 const char *pszNL = (const char *)memchr(pvBuf, '\n', cbLeft);
149 if (pszNL)
150 cb = (uintptr_t)pszNL - (uintptr_t)pvBuf;
151 rc = RTTcpWrite(pDbgcTcp->Sock, pvBuf, cb);
152 }
153 if (RT_FAILURE(rc))
154 {
155 pDbgcTcp->fAlive = false;
156 break;
157 }
158
159 /* advance */
160 cbLeft -= cb;
161 pvBuf = (const char *)pvBuf + cb;
162 }
163
164 /*
165 * Set returned value and return.
166 */
167 if (pcbWritten)
168 *pcbWritten = cbBuf - cbLeft;
169 return rc;
170}
171
172/**
173 * Write (output) - raw version not converting any newlines.
174 *
175 * @returns VBox status code.
176 * @param pBack Pointer to the backend structure supplied by
177 * the backend. The backend can use this to find
178 * it's instance data.
179 * @param pvBuf What to write.
180 * @param cbBuf Number of bytes to write.
181 * @param pcbWritten Where to store the number of bytes actually written.
182 * If NULL the entire buffer must be successfully written.
183 */
184static DECLCALLBACK(int) dbgcTcpBackWriteRaw(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
185{
186 PDBGCTCP pDbgcTcp = DBGCTCP_BACK2DBGCTCP(pBack);
187 if (!pDbgcTcp->fAlive)
188 return VERR_INVALID_HANDLE;
189
190 int rc = RTTcpWrite(pDbgcTcp->Sock, pvBuf, cbBuf);
191 if (RT_FAILURE(rc))
192 pDbgcTcp->fAlive = false;
193
194 if (pcbWritten)
195 *pcbWritten = cbBuf;
196
197 return rc;
198}
199
200/** @copydoc FNDBGCBACKSETREADY */
201static DECLCALLBACK(void) dbgcTcpBackSetReady(PDBGCBACK pBack, bool fReady)
202{
203 /* stub */
204 NOREF(pBack);
205 NOREF(fReady);
206}
207
208
209/**
210 * Serve a TCP Server connection.
211 *
212 * @returns VBox status code.
213 * @returns VERR_TCP_SERVER_STOP to terminate the server loop forcing
214 * the RTTcpCreateServer() call to return.
215 * @param Sock The socket which the client is connected to.
216 * The call will close this socket.
217 * @param pvUser The VM handle.
218 */
219static DECLCALLBACK(int) dbgcTcpConnection(RTSOCKET Sock, void *pvUser)
220{
221 LogFlow(("dbgcTcpConnection: connection! Sock=%d pvUser=%p\n", Sock, pvUser));
222
223 PUVM pUVM = (PUVM)pvUser;
224 PCFGMNODE pKey = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
225 bool fGdbStub = false;
226 int rc = CFGMR3QueryBoolDef(pKey, "GdbStub", &fGdbStub, false);
227 if (RT_FAILURE(rc))
228 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/GdbStub\"");
229
230 /*
231 * Start the console.
232 */
233 DBGCTCP DbgcTcp;
234 DbgcTcp.Back.pfnInput = dbgcTcpBackInput;
235 DbgcTcp.Back.pfnRead = dbgcTcpBackRead;
236 if (fGdbStub)
237 DbgcTcp.Back.pfnWrite = dbgcTcpBackWriteRaw;
238 else
239 DbgcTcp.Back.pfnWrite = dbgcTcpBackWrite;
240 DbgcTcp.Back.pfnSetReady = dbgcTcpBackSetReady;
241 DbgcTcp.fAlive = true;
242 DbgcTcp.Sock = Sock;
243 if (fGdbStub)
244 rc = dbgcGdbStubCreate(pUVM, &DbgcTcp.Back, 0);
245 else
246 rc = DBGCCreate(pUVM, &DbgcTcp.Back, 0);
247 LogFlow(("dbgcTcpConnection: disconnect rc=%Rrc\n", rc));
248 return rc;
249}
250
251
252/**
253 * Spawns a new thread with a TCP based debugging console service.
254 *
255 * @returns VBox status code.
256 * @param pUVM The user mode VM handle.
257 * @param ppvData Where to store a pointer to the instance data.
258 */
259DBGDECL(int) DBGCTcpCreate(PUVM pUVM, void **ppvData)
260{
261 /*
262 * Check what the configuration says.
263 */
264 PCFGMNODE pKey = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
265 bool fEnabled;
266 int rc = CFGMR3QueryBoolDef(pKey, "Enabled", &fEnabled,
267#if defined(VBOX_WITH_DEBUGGER) && defined(VBOX_WITH_DEBUGGER_TCP_BY_DEFAULT) && !defined(__L4ENV__) && !defined(DEBUG_dmik)
268 true
269#else
270 false
271#endif
272 );
273 if (RT_FAILURE(rc))
274 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Enabled\"");
275
276 if (!fEnabled)
277 {
278 LogFlow(("DBGCTcpCreate: returns VINF_SUCCESS (Disabled)\n"));
279 return VINF_SUCCESS;
280 }
281
282 /*
283 * Get the port configuration.
284 */
285 uint32_t u32Port;
286 rc = CFGMR3QueryU32Def(pKey, "Port", &u32Port, 5000);
287 if (RT_FAILURE(rc))
288 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Port\"");
289
290 /*
291 * Get the address configuration.
292 */
293 char szAddress[512];
294 rc = CFGMR3QueryStringDef(pKey, "Address", szAddress, sizeof(szAddress), "");
295 if (RT_FAILURE(rc))
296 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Address\"");
297
298 /*
299 * Create the server (separate thread).
300 */
301 PRTTCPSERVER pServer;
302 rc = RTTcpServerCreate(szAddress, u32Port, RTTHREADTYPE_DEBUGGER, "DBGC", dbgcTcpConnection, pUVM, &pServer);
303 if (RT_SUCCESS(rc))
304 {
305 LogFlow(("DBGCTcpCreate: Created server on port %d %s\n", u32Port, szAddress));
306 *ppvData = pServer;
307 return rc;
308 }
309
310 LogFlow(("DBGCTcpCreate: returns %Rrc\n", rc));
311 return VM_SET_ERROR_U(pUVM, rc, "Cannot start TCP-based debugging console service");
312}
313
314
315/**
316 * Terminates any running TCP base debugger console service.
317 *
318 * @returns VBox status code.
319 * @param pUVM The user mode VM handle.
320 * @param pvData The data returned by DBGCTcpCreate.
321 */
322DBGDECL(int) DBGCTcpTerminate(PUVM pUVM, void *pvData)
323{
324 RT_NOREF1(pUVM);
325
326 /*
327 * Destroy the server instance if any.
328 */
329 if (pvData)
330 {
331 int rc = RTTcpServerDestroy((PRTTCPSERVER)pvData);
332 AssertRC(rc);
333 }
334
335 return VINF_SUCCESS;
336}
337
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette