VirtualBox

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

Last change on this file since 86186 was 86105, checked in by vboxsync, 4 years ago

Debugger/RemoteKd: Add beginnings of debug stub talking the KD remote protocol (WinDbg and friends), heavy work in progress

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.1 KB
Line 
1/* $Id: DBGCTcp.cpp 86105 2020-09-13 08:28:59Z 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 char *pszStubType = NULL;
226 int rc = CFGMR3QueryStringAllocDef(pKey, "StubType", &pszStubType, "Native");
227 if (RT_FAILURE(rc))
228 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/StubType\"");
229
230 /*
231 * Start the console.
232 */
233 DBGCTCP DbgcTcp;
234 DbgcTcp.Back.pfnInput = dbgcTcpBackInput;
235 DbgcTcp.Back.pfnRead = dbgcTcpBackRead;
236 DbgcTcp.Back.pfnSetReady = dbgcTcpBackSetReady;
237 DbgcTcp.fAlive = true;
238 DbgcTcp.Sock = Sock;
239
240 if (!RTStrICmp(pszStubType, "gdb"))
241 {
242 DbgcTcp.Back.pfnWrite = dbgcTcpBackWriteRaw;
243 rc = dbgcGdbStubCreate(pUVM, &DbgcTcp.Back, 0);
244 }
245 else if (!RTStrICmp(pszStubType, "kd"))
246 {
247 DbgcTcp.Back.pfnWrite = dbgcTcpBackWriteRaw;
248 rc = dbgcKdStubCreate(pUVM, &DbgcTcp.Back, 0);
249 }
250 else if (!RTStrICmp(pszStubType, "native"))
251 {
252 DbgcTcp.Back.pfnWrite = dbgcTcpBackWrite;
253 rc = DBGCCreate(pUVM, &DbgcTcp.Back, 0);
254 }
255 else
256 rc = VM_SET_ERROR_U(pUVM, VERR_INVALID_PARAMETER, "Configuration error: \"DBGC/StubType\" contains an invalid type");
257
258 LogFlow(("dbgcTcpConnection: disconnect rc=%Rrc\n", rc));
259 return rc;
260}
261
262
263/**
264 * Spawns a new thread with a TCP based debugging console service.
265 *
266 * @returns VBox status code.
267 * @param pUVM The user mode VM handle.
268 * @param ppvData Where to store a pointer to the instance data.
269 */
270DBGDECL(int) DBGCTcpCreate(PUVM pUVM, void **ppvData)
271{
272 /*
273 * Check what the configuration says.
274 */
275 PCFGMNODE pKey = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
276 bool fEnabled;
277 int rc = CFGMR3QueryBoolDef(pKey, "Enabled", &fEnabled,
278#if defined(VBOX_WITH_DEBUGGER) && defined(VBOX_WITH_DEBUGGER_TCP_BY_DEFAULT) && !defined(__L4ENV__) && !defined(DEBUG_dmik)
279 true
280#else
281 false
282#endif
283 );
284 if (RT_FAILURE(rc))
285 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Enabled\"");
286
287 if (!fEnabled)
288 {
289 LogFlow(("DBGCTcpCreate: returns VINF_SUCCESS (Disabled)\n"));
290 return VINF_SUCCESS;
291 }
292
293 /*
294 * Get the port configuration.
295 */
296 uint32_t u32Port;
297 rc = CFGMR3QueryU32Def(pKey, "Port", &u32Port, 5000);
298 if (RT_FAILURE(rc))
299 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Port\"");
300
301 /*
302 * Get the address configuration.
303 */
304 char szAddress[512];
305 rc = CFGMR3QueryStringDef(pKey, "Address", szAddress, sizeof(szAddress), "");
306 if (RT_FAILURE(rc))
307 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Address\"");
308
309 /*
310 * Create the server (separate thread).
311 */
312 PRTTCPSERVER pServer;
313 rc = RTTcpServerCreate(szAddress, u32Port, RTTHREADTYPE_DEBUGGER, "DBGC", dbgcTcpConnection, pUVM, &pServer);
314 if (RT_SUCCESS(rc))
315 {
316 LogFlow(("DBGCTcpCreate: Created server on port %d %s\n", u32Port, szAddress));
317 *ppvData = pServer;
318 return rc;
319 }
320
321 LogFlow(("DBGCTcpCreate: returns %Rrc\n", rc));
322 return VM_SET_ERROR_U(pUVM, rc, "Cannot start TCP-based debugging console service");
323}
324
325
326/**
327 * Terminates any running TCP base debugger console service.
328 *
329 * @returns VBox status code.
330 * @param pUVM The user mode VM handle.
331 * @param pvData The data returned by DBGCTcpCreate.
332 */
333DBGDECL(int) DBGCTcpTerminate(PUVM pUVM, void *pvData)
334{
335 RT_NOREF1(pUVM);
336
337 /*
338 * Destroy the server instance if any.
339 */
340 if (pvData)
341 {
342 int rc = RTTcpServerDestroy((PRTTCPSERVER)pvData);
343 AssertRC(rc);
344 }
345
346 return VINF_SUCCESS;
347}
348
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