VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCIo.cpp@ 96014

Last change on this file since 96014 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: DBGCIo.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, I/O provider handling.
4 */
5
6/*
7 * Copyright (C) 2020-2022 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/mem.h>
27#include <iprt/thread.h>
28#include <VBox/log.h>
29#include <iprt/assert.h>
30
31#include <iprt/string.h>
32
33#include "DBGCIoProvInternal.h"
34#include "DBGCInternal.h"
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40
41/**
42 * Stub descriptor.
43 */
44typedef struct DBGCSTUB
45{
46 /** Name of the stub. */
47 const char *pszName;
48 /** Flag whether this is an ASCII based protocol which requires some newline handling. */
49 bool fAscii;
50 /**
51 * The runloop callback.
52 *
53 * @returns VBox status code.
54 * @param pUVM The user mode VM handle.
55 * @param pIo Pointer to the I/O callback table.
56 * @param fFlags Flags for the runloop, MBZ for now.
57 */
58 DECLCALLBACKMEMBER(int, pfnRunloop, (PUVM pUVM, PCDBGCIO pIo, unsigned fFlags));
59} DBGCSTUB;
60/** Pointer to a stub descriptor. */
61typedef DBGCSTUB *PDBGCSTUB;
62/** Pointer to a const stub descriptor. */
63typedef const DBGCSTUB *PCDBGCSTUB;
64
65
66/** Pointer to the instance data of the debug console I/O. */
67typedef struct DBGCIOINT *PDBGCIOINT;
68
69
70/**
71 * A single debug console I/O service.
72 */
73typedef struct DBGCIOSVC
74{
75 /** Pointer to the owning structure. */
76 PDBGCIOINT pDbgcIo;
77 /** The user mode VM handle this service belongs to. */
78 PUVM pUVM;
79 /** The I/O provider registration record for this service. */
80 PCDBGCIOPROVREG pIoProvReg;
81 /** The I/O provider instance. */
82 DBGCIOPROV hDbgcIoProv;
83 /** The stub type. */
84 PCDBGCSTUB pStub;
85 /** The thread managing the service. */
86 RTTHREAD hThreadSvc;
87 /** Pointer to the I/O callback table currently being served. */
88 PCDBGCIO pIo;
89 /** The wrapping DBGC I/O callback table for ASCII based protocols. */
90 DBGCIO IoAscii;
91} DBGCIOSVC;
92/** Pointer to a single debug console I/O service. */
93typedef DBGCIOSVC *PDBGCIOSVC;
94/** Poitner to a const single debug console I/O service. */
95typedef const DBGCIOSVC *PCDBGCIOSVC;
96
97
98/**
99 * Debug console I/O instance data.
100 */
101typedef struct DBGCIOINT
102{
103 /** Number of configured I/O service instances. */
104 volatile uint32_t cSvcsCfg;
105 /** Number of running I/O service instances. */
106 volatile uint32_t cSvcsRunning;
107 /** Flag whether the services were asked to shut down. */
108 volatile bool fShutdown;
109 /** Array of active I/O service instances. */
110 RT_FLEXIBLE_ARRAY_EXTENSION
111 DBGCIOSVC aSvc[RT_FLEXIBLE_ARRAY];
112} DBGCIOINT;
113
114
115/*********************************************************************************************************************************
116* Global Variables *
117*********************************************************************************************************************************/
118
119/**
120 * Array of supported I/O providers.
121 */
122static PCDBGCIOPROVREG g_aIoProv[] =
123{
124 &g_DbgcIoProvTcp,
125 &g_DbgcIoProvIpc
126};
127
128
129static DECLCALLBACK(int) dbgcIoNativeStubRunloop(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags);
130
131/**
132 * Array of supported stubs.
133 */
134static const DBGCSTUB g_aStubs[] =
135{
136 /** pszName fAscii pfnRunloop */
137 { "Native", true, dbgcIoNativeStubRunloop },
138 { "Gdb", false, dbgcGdbStubRunloop },
139 { "Kd", false, dbgcKdStubRunloop }
140};
141
142
143/*********************************************************************************************************************************
144* Internal Functions *
145*********************************************************************************************************************************/
146
147
148/**
149 * Destroys all allocated data for the given dbeugger console I/O instance.
150 *
151 * @returns nothing.
152 * @param pDbgcIo Pointer to the dbeugger console I/O instance data.
153 */
154static void dbgcIoDestroy(PDBGCIOINT pDbgcIo)
155{
156 for (uint32_t i = 0; i < pDbgcIo->cSvcsCfg; i++)
157 {
158 PDBGCIOSVC pIoSvc = &pDbgcIo->aSvc[i];
159
160 if (pIoSvc->hThreadSvc != NIL_RTTHREAD)
161 {
162 int rc = RTThreadWait(pIoSvc->hThreadSvc, RT_MS_10SEC, NULL /*prc*/);
163 AssertRC(rc);
164
165 pIoSvc->hThreadSvc = NIL_RTTHREAD;
166 pIoSvc->pIoProvReg->pfnDestroy(pIoSvc->hDbgcIoProv);
167 }
168 }
169
170 RTMemFree(pDbgcIo);
171}
172
173
174/**
175 * Returns the number of I/O services configured.
176 *
177 * @returns I/O service count.
178 * @param pCfgRoot The root of the config.
179 */
180static uint32_t dbgcIoGetSvcCount(PCFGMNODE pCfgRoot)
181{
182 uint32_t cSvcs = 0;
183 PCFGMNODE pNd = CFGMR3GetFirstChild(pCfgRoot);
184 while (pNd)
185 {
186 cSvcs++;
187 pNd = CFGMR3GetNextChild(pNd);
188 }
189
190 return cSvcs;
191}
192
193
194/**
195 * Returns a pointer to the I/O provider registration record matching the given name.
196 *
197 * @returns Pointer to the registration record or NULL if not found.
198 * @param pszName The name to look for (case insensitive matching).
199 */
200static PCDBGCIOPROVREG dbgcIoProvFindRegByName(const char *pszName)
201{
202 for (uint32_t i = 0; i < RT_ELEMENTS(g_aIoProv); i++)
203 {
204 if (!RTStrICmp(g_aIoProv[i]->pszName, pszName))
205 return g_aIoProv[i];
206 }
207 return NULL;
208}
209
210
211/**
212 * Returns a pointer to the stub record matching the given name.
213 *
214 * @returns Pointer to the stub record or NULL if not found.
215 * @param pszName The name to look for (case insensitive matching).
216 */
217static PCDBGCSTUB dbgcIoFindStubByName(const char *pszName)
218{
219 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStubs); i++)
220 {
221 if (!RTStrICmp(g_aStubs[i].pszName, pszName))
222 return &g_aStubs[i];
223 }
224 return NULL;
225}
226
227
228/**
229 * Wrapper around DBGCCreate() to get it working as a callback.
230 */
231static DECLCALLBACK(int) dbgcIoNativeStubRunloop(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags)
232{
233 return DBGCCreate(pUVM, pIo, fFlags);
234}
235
236
237/**
238 * @interface_method_impl{DBGCIO,pfnDestroy}
239 */
240static DECLCALLBACK(void) dbgcIoAsciiDestroy(PCDBGCIO pIo)
241{
242 PDBGCIOSVC pIoSvc = RT_FROM_MEMBER(pIo, DBGCIOSVC, IoAscii);
243 pIoSvc->pIo->pfnDestroy(pIoSvc->pIo);
244}
245
246
247/**
248 * @interface_method_impl{DBGCIO,pfnInput}
249 */
250static DECLCALLBACK(bool) dbgcIoAsciiInput(PCDBGCIO pIo, uint32_t cMillies)
251{
252 PDBGCIOSVC pIoSvc = RT_FROM_MEMBER(pIo, DBGCIOSVC, IoAscii);
253 return pIoSvc->pIo->pfnInput(pIoSvc->pIo, cMillies);
254}
255
256
257/**
258 * @interface_method_impl{DBGCIO,pfnRead}
259 */
260static DECLCALLBACK(int) dbgcIoAsciiRead(PCDBGCIO pIo, void *pvBuf, size_t cbBuf, size_t *pcbRead)
261{
262 PDBGCIOSVC pIoSvc = RT_FROM_MEMBER(pIo, DBGCIOSVC, IoAscii);
263 return pIoSvc->pIo->pfnRead(pIoSvc->pIo, pvBuf, cbBuf, pcbRead);
264}
265
266
267/**
268 * @interface_method_impl{DBGCIO,pfnWrite}
269 */
270static DECLCALLBACK(int) dbgcIoAsciiWrite(PCDBGCIO pIo, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
271{
272 PDBGCIOSVC pIoSvc = RT_FROM_MEMBER(pIo, DBGCIOSVC, IoAscii);
273
274 /*
275 * convert '\n' to '\r\n' while writing.
276 */
277 int rc = 0;
278 size_t cbLeft = cbBuf;
279 while (cbLeft)
280 {
281 size_t cb = cbLeft;
282 /* write newlines */
283 if (*(const char *)pvBuf == '\n')
284 {
285 rc = pIoSvc->pIo->pfnWrite(pIoSvc->pIo, "\r\n", 2, NULL);
286 cb = 1;
287 }
288 /* write till next newline */
289 else
290 {
291 const char *pszNL = (const char *)memchr(pvBuf, '\n', cbLeft);
292 if (pszNL)
293 cb = (uintptr_t)pszNL - (uintptr_t)pvBuf;
294 rc = pIoSvc->pIo->pfnWrite(pIoSvc->pIo, pvBuf, cb, NULL);
295 }
296 if (RT_FAILURE(rc))
297 break;
298
299 /* advance */
300 cbLeft -= cb;
301 pvBuf = (const char *)pvBuf + cb;
302 }
303
304 /*
305 * Set returned value and return.
306 */
307 if (pcbWritten)
308 *pcbWritten = cbBuf - cbLeft;
309 return rc;
310}
311
312
313/**
314 * @interface_method_impl{DBGCIO,pfnSetReady}
315 */
316static DECLCALLBACK(void) dbgcIoAsciiSetReady(PCDBGCIO pIo, bool fReady)
317{
318 PDBGCIOSVC pIoSvc = RT_FROM_MEMBER(pIo, DBGCIOSVC, IoAscii);
319 return pIoSvc->pIo->pfnSetReady(pIoSvc->pIo, fReady);
320}
321
322
323/**
324 * The I/O thread handling the service.
325 */
326static DECLCALLBACK(int) dbgcIoSvcThread(RTTHREAD hThreadSelf, void *pvUser)
327{
328 RT_NOREF(hThreadSelf);
329
330 int rc = VINF_SUCCESS;
331 PDBGCIOSVC pIoSvc = (PDBGCIOSVC)pvUser;
332 PDBGCIOINT pDbgcIo = pIoSvc->pDbgcIo;
333 PCDBGCIOPROVREG pIoProvReg = pIoSvc->pIoProvReg;
334
335 while (!ASMAtomicReadBool(&pDbgcIo->fShutdown))
336 {
337 /* Wait until someone connects. */
338 rc = pIoProvReg->pfnWaitForConnect(pIoSvc->hDbgcIoProv, RT_INDEFINITE_WAIT, &pIoSvc->pIo);
339 if (RT_SUCCESS(rc))
340 {
341 PCDBGCIO pIo = pIoSvc->pIo;
342
343 if (pIoSvc->pStub->fAscii)
344 {
345 pIoSvc->IoAscii.pfnDestroy = dbgcIoAsciiDestroy;
346 pIoSvc->IoAscii.pfnInput = dbgcIoAsciiInput;
347 pIoSvc->IoAscii.pfnRead = dbgcIoAsciiRead;
348 pIoSvc->IoAscii.pfnWrite = dbgcIoAsciiWrite;
349 pIoSvc->IoAscii.pfnSetReady = dbgcIoAsciiSetReady;
350 pIo = &pIoSvc->IoAscii;
351 }
352
353 /* call the runloop for the connection. */
354 pIoSvc->pStub->pfnRunloop(pIoSvc->pUVM, pIo, 0 /*fFlags*/);
355
356 pIo->pfnDestroy(pIo);
357 }
358 else if ( rc != VERR_TIMEOUT
359 && rc != VERR_INTERRUPTED)
360 break;
361 }
362
363 if (!ASMAtomicDecU32(&pDbgcIo->cSvcsRunning))
364 dbgcIoDestroy(pDbgcIo);
365
366 return rc;
367}
368
369
370static int dbgcIoSvcInitWorker(PUVM pUVM, PDBGCIOSVC pIoSvc, PCDBGCIOPROVREG pIoProvReg,
371 PCDBGCSTUB pStub, PCFGMNODE pCfg, const char *pszName,
372 bool fIgnoreNetAddrInUse)
373{
374 pIoSvc->pUVM = pUVM;
375 pIoSvc->pIoProvReg = pIoProvReg;
376 pIoSvc->pStub = pStub;
377
378 /* Create the provider instance and spawn the dedicated thread handling that service. */
379 int rc = pIoProvReg->pfnCreate(&pIoSvc->hDbgcIoProv, pCfg);
380 if (RT_SUCCESS(rc))
381 {
382 rc = RTThreadCreateF(&pIoSvc->hThreadSvc, dbgcIoSvcThread, pIoSvc, 0 /*cbStack*/,
383 RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "DbgcThrd-%s", pszName);
384 if (RT_SUCCESS(rc))
385 {
386 ASMAtomicIncU32(&pIoSvc->pDbgcIo->cSvcsRunning);
387 return VINF_SUCCESS;
388 }
389 else
390 rc = VMR3SetError(pUVM, rc, RT_SRC_POS,
391 "Configuration error: Creating an instance of the service \"%s\" failed",
392 pszName);
393
394 pIoProvReg->pfnDestroy(pIoSvc->hDbgcIoProv);
395 }
396 else if ( rc != VERR_NET_ADDRESS_IN_USE
397 || !fIgnoreNetAddrInUse)
398 rc = VMR3SetError(pUVM, rc, RT_SRC_POS,
399 "Configuration error: Creating an instance of the I/O provider \"%s\" failed",
400 pIoProvReg->pszName);
401
402 return rc;
403}
404
405
406/**
407 * Tries to initialize the given I/O service from the given config.
408 *
409 * @returns VBox status code.
410 * @param pUVM The user mode VM handle.
411 * @param pIoSvc The I/O service instance to initialize.
412 * @param pCfg The config for the instance.
413 */
414static int dbgcIoSvcInit(PUVM pUVM, PDBGCIOSVC pIoSvc, PCFGMNODE pCfg)
415{
416 char szName[32 + 1]; RT_ZERO(szName);
417 int rc = CFGMR3GetName(pCfg, &szName[0], sizeof(szName));
418 if (RT_SUCCESS(rc))
419 {
420 char szIoProvName[32 + 1]; RT_ZERO(szIoProvName);
421 rc = CFGMR3QueryString(pCfg, "Provider", &szIoProvName[0], sizeof(szIoProvName));
422 if (RT_SUCCESS(rc))
423 {
424 char szStub[32 + 1]; RT_ZERO(szStub);
425 rc = CFGMR3QueryString(pCfg, "StubType", &szStub[0], sizeof(szStub));
426 if (RT_SUCCESS(rc))
427 {
428 PCDBGCIOPROVREG pIoProvReg = dbgcIoProvFindRegByName(szIoProvName);
429 if (pIoProvReg)
430 {
431 PCDBGCSTUB pStub = dbgcIoFindStubByName(szStub);
432 if (pStub)
433 rc = dbgcIoSvcInitWorker(pUVM, pIoSvc, pIoProvReg, pStub, pCfg, szName,
434 false /*fIgnoreNetAddrInUse*/);
435 else
436 rc = VMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, "Configuration error: The stub type \"%s\" could not be found",
437 szStub);
438 }
439 else
440 rc = VMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, "Configuration error: The provider \"%s\" could not be found",
441 szIoProvName);
442 }
443 else
444 rc = VM_SET_ERROR_U(pUVM, rc, "Configuration error: Querying \"StubType\" failed");
445 }
446 else
447 rc = VM_SET_ERROR_U(pUVM, rc, "Configuration error: Querying \"Provider\" failed");
448 }
449 else
450 rc = VM_SET_ERROR_U(pUVM, rc, "Configuration error: Querying service identifier failed (maybe too long)");
451
452 return rc;
453}
454
455
456/**
457 * Creates the DBGC I/O services from the legacy TCP config.
458 *
459 * @returns VBox status code.
460 * @param pUVM The user mode VM handle.
461 * @param pKey The config key.
462 * @param ppvData Where to store the I/o instance data on success.
463 */
464static int dbgcIoCreateLegacyTcp(PUVM pUVM, PCFGMNODE pKey, void **ppvData)
465{
466 bool fEnabled;
467 int rc = CFGMR3QueryBoolDef(pKey, "Enabled", &fEnabled,
468#if defined(VBOX_WITH_DEBUGGER) && defined(VBOX_WITH_DEBUGGER_TCP_BY_DEFAULT)
469 true
470#else
471 false
472#endif
473 );
474 if (RT_FAILURE(rc))
475 return VM_SET_ERROR_U(pUVM, rc, "Configuration error: Failed querying \"DBGC/Enabled\"");
476
477 if (!fEnabled)
478 {
479 LogFlow(("DBGCTcpCreate: returns VINF_SUCCESS (Disabled)\n"));
480 return VINF_SUCCESS;
481 }
482
483 PDBGCIOINT pDbgcIo = (PDBGCIOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGCIOINT, aSvc[1]));
484 if (RT_LIKELY(pDbgcIo))
485 {
486 pDbgcIo->aSvc[0].pDbgcIo = pDbgcIo;
487 pDbgcIo->cSvcsCfg = 1;
488 pDbgcIo->cSvcsRunning = 1;
489 rc = dbgcIoSvcInitWorker(pUVM, &pDbgcIo->aSvc[0], &g_DbgcIoProvTcp, &g_aStubs[0], pKey, "TCP",
490 true /*fIgnoreNetAddrInUse*/);
491 if (RT_SUCCESS(rc))
492 {
493 *ppvData = pDbgcIo;
494 return VINF_SUCCESS;
495 }
496
497 RTMemFree(pDbgcIo);
498 if (rc == VERR_NET_ADDRESS_IN_USE)
499 rc = VINF_SUCCESS;
500 }
501 else
502 rc = VERR_NO_MEMORY;
503
504 if (RT_FAILURE(rc))
505 rc = VM_SET_ERROR_U(pUVM, rc, "Cannot start TCP-based debugging console service");
506 return rc;
507}
508
509
510/**
511 * Sets up debugger I/O based on the VM config.
512 *
513 * @returns VBox status code.
514 * @param pUVM The user mode VM handle.
515 * @param ppvData Where to store a pointer to the instance data.
516 */
517DBGDECL(int) DBGCIoCreate(PUVM pUVM, void **ppvData)
518{
519 /*
520 * Check what the configuration says.
521 */
522 PCFGMNODE pKey = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
523 uint32_t cSvcs = dbgcIoGetSvcCount(pKey);
524 int rc = VINF_SUCCESS;
525
526 /* If no services are configured try the legacy config supporting TCP only. */
527 if (cSvcs)
528 {
529 PDBGCIOINT pDbgcIo = (PDBGCIOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGCIOINT, aSvc[cSvcs]));
530 if (RT_LIKELY(pDbgcIo))
531 {
532 pDbgcIo->cSvcsCfg = 0;
533 pDbgcIo->cSvcsRunning = 1;
534 pDbgcIo->fShutdown = false;
535
536 for (uint32_t i = 0; i < cSvcs; i++)
537 pDbgcIo->aSvc[i].hThreadSvc = NIL_RTTHREAD;
538
539 PCFGMNODE pSvcCfg = CFGMR3GetFirstChild(pKey);
540 for (uint32_t i = 0; i < cSvcs && RT_SUCCESS(rc); i++)
541 {
542 pDbgcIo->aSvc[i].pDbgcIo = pDbgcIo;
543
544 rc = dbgcIoSvcInit(pUVM, &pDbgcIo->aSvc[i], pSvcCfg);
545 if (RT_SUCCESS(rc))
546 pDbgcIo->cSvcsCfg++;
547 else
548 rc = VM_SET_ERROR_U(pUVM, rc, "Failed to initialize the debugger I/O service");
549
550 pSvcCfg = CFGMR3GetNextChild(pSvcCfg);
551 }
552
553 if (RT_SUCCESS(rc))
554 *ppvData = pDbgcIo;
555 else
556 {
557 if (!ASMAtomicDecU32(&pDbgcIo->cSvcsRunning))
558 dbgcIoDestroy(pDbgcIo);
559 }
560 }
561 else
562 rc = VM_SET_ERROR_U(pUVM, VERR_NO_MEMORY, "Failed to allocate memory for the debugger I/O service");
563 }
564 else
565 rc = dbgcIoCreateLegacyTcp(pUVM, pKey, ppvData);
566
567 return rc;
568}
569
570
571/**
572 * Terminates any running debugger services.
573 *
574 * @returns VBox status code.
575 * @param pUVM The user mode VM handle.
576 * @param pvData The data returned by DBGCIoCreate.
577 */
578DBGDECL(int) DBGCIoTerminate(PUVM pUVM, void *pvData)
579{
580 RT_NOREF(pUVM);
581 PDBGCIOINT pDbgcIo = (PDBGCIOINT)pvData;
582
583 if (pDbgcIo)
584 {
585 ASMAtomicXchgBool(&pDbgcIo->fShutdown, true);
586
587 for (uint32_t i = 0; i < pDbgcIo->cSvcsCfg; i++)
588 {
589 PDBGCIOSVC pIoSvc = &pDbgcIo->aSvc[i];
590
591 if (pIoSvc->hThreadSvc != NIL_RTTHREAD)
592 pIoSvc->pIoProvReg->pfnWaitInterrupt(pIoSvc->hDbgcIoProv);
593 }
594
595 if (!ASMAtomicDecU32(&pDbgcIo->cSvcsRunning))
596 dbgcIoDestroy(pDbgcIo);
597 }
598
599 return VINF_SUCCESS;
600}
601
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