VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3PlugIn.cpp@ 94617

Last change on this file since 94617 was 93470, checked in by vboxsync, 3 years ago

DbgPlugInDiggers,VMM,Main: Refactored the diggers and related interfaces to work via the VMM function table. Removed non-working tstVBoxDbg (needs proper COM now). bugref:10072

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: DBGFR3PlugIn.cpp 93470 2022-01-27 23:51:28Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Plug-In Support.
4 */
5
6/*
7 * Copyright (C) 2008-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#define LOG_GROUP LOG_GROUP_DBGF
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/mm.h>
25#include <VBox/vmm/vmm.h>
26#include "DBGFInternal.h"
27#include <VBox/vmm/uvm.h>
28#include <VBox/vmm/vm.h>
29#include <VBox/err.h>
30#include <VBox/log.h>
31#include <VBox/version.h>
32
33#include <iprt/alloca.h>
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36#include <iprt/env.h>
37#include <iprt/dir.h>
38#include <iprt/ldr.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46
47#define DBGF_PLUG_IN_READ_LOCK(pUVM) \
48 do { int rcLock = RTCritSectRwEnterShared(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
49#define DBGF_PLUG_IN_READ_UNLOCK(pUVM) \
50 do { int rcLock = RTCritSectRwLeaveShared(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
51
52#define DBGF_PLUG_IN_WRITE_LOCK(pUVM) \
53 do { int rcLock = RTCritSectRwEnterExcl(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
54#define DBGF_PLUG_IN_WRITE_UNLOCK(pUVM) \
55 do { int rcLock = RTCritSectRwLeaveExcl(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
56
57/** Max allowed length of a plug-in name (excludes the path and suffix). */
58#define DBGFPLUGIN_MAX_NAME 64
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64/**
65 * Plug-in tracking record.
66 */
67typedef struct DBGFPLUGIN
68{
69 /** Pointer to the next plug-in. */
70 struct DBGFPLUGIN *pNext;
71 /** The loader handle. */
72 RTLDRMOD hLdrMod;
73 /** The plug-in entry point. */
74 PFNDBGFPLUGIN pfnEntry;
75 /** The name length. */
76 uint8_t cchName;
77 /** The plug-in name (variable length). */
78 char szName[1];
79} DBGFPLUGIN;
80/** Pointer to plug-in tracking record. */
81typedef DBGFPLUGIN *PDBGFPLUGIN;
82
83
84/*********************************************************************************************************************************
85* Internal Functions *
86*********************************************************************************************************************************/
87static DECLCALLBACK(void) dbgfPlugInUnloadAll(PUVM pUVM);
88static FNDBGFHANDLERINT dbgfR3PlugInInfoList;
89
90
91/**
92 * Internal init routine called by DBGFR3Init().
93 *
94 * @returns VBox status code.
95 * @param pUVM The user mode VM handle.
96 */
97int dbgfR3PlugInInit(PUVM pUVM)
98{
99 return DBGFR3InfoRegisterInternal(pUVM->pVM, "plugins", "Lists the debugger plug-ins.", dbgfR3PlugInInfoList);
100}
101
102
103/**
104 * Internal cleanup routine called by DBGFR3Term().
105 *
106 * @param pUVM The user mode VM handle.
107 */
108void dbgfR3PlugInTerm(PUVM pUVM)
109{
110 dbgfPlugInUnloadAll(pUVM);
111}
112
113
114/**
115 * Extracts the plug-in name from a plug-in specifier that may or may not
116 * include path and/or suffix.
117 *
118 * @returns VBox status code.
119 *
120 * @param pszDst Where to return the name. At least DBGFPLUGIN_MAX_NAME
121 * worth of buffer space.
122 * @param pszPlugIn The plug-in module specifier to parse.
123 * @param pErrInfo Optional error information structure.
124 */
125static int dbgfPlugInExtractName(char *pszDst, const char *pszPlugIn, PRTERRINFO pErrInfo)
126{
127 /*
128 * Parse out the name stopping at the extension.
129 */
130 const char *pszName = RTPathFilename(pszPlugIn);
131 if (!pszName || !*pszName)
132 return VERR_INVALID_NAME;
133 if (!RTStrNICmp(pszName, RT_STR_TUPLE(DBGF_PLUG_IN_PREFIX)))
134 {
135 pszName += sizeof(DBGF_PLUG_IN_PREFIX) - 1;
136 if (!*pszName)
137 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: nothing after the prefix");
138 }
139
140 int ch;
141 size_t cchName = 0;
142 while ( (ch = pszName[cchName]) != '\0'
143 && ch != '.')
144 {
145 if ( RT_C_IS_ALPHA(ch)
146 || (RT_C_IS_DIGIT(ch) && cchName != 0))
147 cchName++;
148 else
149 {
150 if (!RT_C_IS_DIGIT(ch))
151 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: '%c' is not alphanumeric", ch);
152 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME,
153 "Invalid plug-in name: Cannot start with a digit (after the prefix)");
154 }
155 }
156
157 if (cchName >= DBGFPLUGIN_MAX_NAME)
158 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: too long (max %u)", DBGFPLUGIN_MAX_NAME);
159
160 /*
161 * We're very picky about the extension when present.
162 */
163 if ( ch == '.'
164 && RTStrICmp(&pszName[cchName], RTLdrGetSuff()))
165 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME,
166 "Invalid plug-in name: Suffix isn't the default dll/so/dylib one (%s): '%s'",
167 RTLdrGetSuff(), &pszName[cchName]);
168
169 /*
170 * Copy it.
171 */
172 memcpy(pszDst, pszName, cchName);
173 pszDst[cchName] = '\0';
174 return VINF_SUCCESS;
175}
176
177
178/**
179 * Locate a loaded plug-in.
180 *
181 * @returns Pointer to the plug-in tracking structure.
182 * @param pUVM Pointer to the user-mode VM structure.
183 * @param pszName The name of the plug-in we're looking for.
184 * @param ppPrev Where to optionally return the pointer to the
185 * previous list member.
186 */
187static PDBGFPLUGIN dbgfR3PlugInLocate(PUVM pUVM, const char *pszName, PDBGFPLUGIN *ppPrev)
188{
189 PDBGFPLUGIN pPrev = NULL;
190 PDBGFPLUGIN pCur = pUVM->dbgf.s.pPlugInHead;
191 while (pCur)
192 {
193 if (!RTStrICmp(pCur->szName, pszName))
194 {
195 if (ppPrev)
196 *ppPrev = pPrev;
197 return pCur;
198 }
199
200 /* advance */
201 pPrev = pCur;
202 pCur = pCur->pNext;
203 }
204 return NULL;
205}
206
207
208/**
209 * Try load the specified plug-in module.
210 *
211 * @returns VINF_SUCCESS on success, path error or loader error on failure.
212 *
213 * @param pPlugIn The plug-in tracing record.
214 * @param pszModule Module name.
215 * @param pErrInfo Optional error information structure.
216 */
217static int dbgfR3PlugInTryLoad(PDBGFPLUGIN pPlugIn, const char *pszModule, PRTERRINFO pErrInfo)
218{
219 /*
220 * Load it and try resolve the entry point.
221 */
222 int rc = SUPR3HardenedVerifyPlugIn(pszModule, pErrInfo);
223 if (RT_SUCCESS(rc))
224 rc = RTLdrLoadEx(pszModule, &pPlugIn->hLdrMod, RTLDRLOAD_FLAGS_LOCAL, pErrInfo);
225 if (RT_SUCCESS(rc))
226 {
227 rc = RTLdrGetSymbol(pPlugIn->hLdrMod, DBGF_PLUG_IN_ENTRYPOINT, (void **)&pPlugIn->pfnEntry);
228 if (RT_SUCCESS(rc))
229 {
230 LogRel(("DBGF: Loaded Plug-In '%s' (%s)\n", pPlugIn->szName, pszModule));
231 return VINF_SUCCESS;
232 }
233
234 RTErrInfoSet(pErrInfo, rc, "Failed to locate plug-in entrypoint (" DBGF_PLUG_IN_ENTRYPOINT ")" );
235 LogRel(("DBGF: RTLdrGetSymbol('%s', '%s',) -> %Rrc\n", pszModule, DBGF_PLUG_IN_ENTRYPOINT, rc));
236
237 RTLdrClose(pPlugIn->hLdrMod);
238 pPlugIn->hLdrMod = NIL_RTLDRMOD;
239 }
240 return rc;
241}
242
243
244/**
245 * RTPathTraverseList callback.
246 *
247 * @returns See FNRTPATHTRAVERSER.
248 *
249 * @param pchPath See FNRTPATHTRAVERSER.
250 * @param cchPath See FNRTPATHTRAVERSER.
251 * @param pvUser1 The plug-in specifier.
252 * @param pvUser2 The plug-in tracking record.
253 */
254static DECLCALLBACK(int) dbgfR3PlugInLoadCallback(const char *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
255{
256 PDBGFPLUGIN pPlugIn = (PDBGFPLUGIN)pvUser1;
257 PRTERRINFO pErrInfo = (PRTERRINFO)pvUser2;
258
259 /*
260 * Join the path and the specified plug-in name, adding prefix and suffix.
261 */
262 const char *pszSuff = RTLdrGetSuff();
263 size_t const cchSuff = strlen(pszSuff);
264 size_t const cchModule = cchPath + sizeof(RTPATH_SLASH_STR) + sizeof(DBGF_PLUG_IN_PREFIX) + pPlugIn->cchName + cchSuff + 4;
265 char *pszModule = (char *)alloca(cchModule);
266 AssertReturn(pszModule, VERR_TRY_AGAIN);
267 memcpy(pszModule, pchPath, cchPath);
268 pszModule[cchPath] = '\0';
269
270 int rc = RTPathAppend(pszModule, cchModule, DBGF_PLUG_IN_PREFIX);
271 AssertRCReturn(rc, VERR_TRY_AGAIN);
272 strcat(&pszModule[cchPath], pPlugIn->szName);
273 strcat(&pszModule[cchPath + sizeof(DBGF_PLUG_IN_PREFIX) - 1 + pPlugIn->cchName], pszSuff);
274 Assert(strlen(pszModule) < cchModule - 4);
275
276 if (RTPathExists(pszModule))
277 {
278 rc = dbgfR3PlugInTryLoad(pPlugIn, pszModule, pErrInfo);
279 if (RT_SUCCESS(rc))
280 return VINF_SUCCESS;
281 }
282
283 return VERR_TRY_AGAIN;
284}
285
286
287/**
288 * Loads a plug-in.
289 *
290 * @returns VBox status code.
291 * @param pUVM Pointer to the user-mode VM structure.
292 * @param pszName The plug-in name.
293 * @param pszMaybeModule Path to the plug-in, or just the
294 * plug-in name as specified by the user. Ignored
295 * if no path.
296 * @param pErrInfo Optional error information structure.
297 */
298static DECLCALLBACK(int) dbgfR3PlugInLoad(PUVM pUVM, const char *pszName, const char *pszMaybeModule, PRTERRINFO pErrInfo)
299{
300 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
301
302 /*
303 * Check if a plug-in by the given name already exists.
304 */
305 PDBGFPLUGIN pPlugIn = dbgfR3PlugInLocate(pUVM, pszName, NULL);
306 if (pPlugIn)
307 {
308 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
309 return RTErrInfoSetF(pErrInfo, VERR_ALREADY_EXISTS, "A plug-in by the name '%s' already exists", pszName);
310 }
311
312 /*
313 * Create a module structure and we can pass around via RTPathTraverseList if needed.
314 */
315 size_t cbName = strlen(pszName) + 1;
316 pPlugIn = (PDBGFPLUGIN)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF, RT_UOFFSETOF_DYN(DBGFPLUGIN, szName[cbName]));
317 if (RT_UNLIKELY(!pPlugIn))
318 {
319 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
320 return VERR_NO_MEMORY;
321 }
322 memcpy(pPlugIn->szName, pszName, cbName);
323 pPlugIn->cchName = (uint8_t)cbName - 1;
324 Assert(pPlugIn->cchName == cbName - 1);
325
326 /*
327 * If the caller specified a path, try load exactly what was specified.
328 */
329 int rc;
330 if (RTPathHavePath(pszMaybeModule))
331 rc = dbgfR3PlugInTryLoad(pPlugIn, pszMaybeModule, pErrInfo);
332 else
333 {
334 /*
335 * No path specified, search for the plug-in using the canonical
336 * module name for it.
337 */
338 RTErrInfoClear(pErrInfo);
339
340 /* 1. The private architecture directory. */
341 char szPath[_4K];
342 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
343 if (RT_SUCCESS(rc))
344 rc = RTPathTraverseList(szPath, '\0', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
345 if (RT_FAILURE_NP(rc))
346 {
347 /* 2. The config value 'PlugInPath' */
348 int rc2 = CFGMR3QueryString(CFGMR3GetChild(CFGMR3GetRootU(pUVM), "/DBGF"), "PlugInPath", szPath, sizeof(szPath));
349 if (RT_SUCCESS(rc2))
350 rc = RTPathTraverseList(szPath, ';', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
351 if (RT_FAILURE_NP(rc))
352 {
353 /* 3. The VBOXDBG_PLUG_IN_PATH environment variable. */
354 rc2 = RTEnvGetEx(RTENV_DEFAULT, "VBOXDBG_PLUG_IN_PATH", szPath, sizeof(szPath), NULL);
355 if (RT_SUCCESS(rc2))
356 rc = RTPathTraverseList(szPath, ';', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
357 }
358 }
359
360 if (rc == VERR_END_OF_STRING)
361 rc = VERR_FILE_NOT_FOUND;
362 if (pErrInfo && !RTErrInfoIsSet(pErrInfo))
363 RTErrInfoSetF(pErrInfo, rc, "Failed to locate '%s'", pPlugIn->szName);
364 }
365 if (RT_SUCCESS(rc))
366 {
367 /*
368 * Try initialize it.
369 */
370 rc = pPlugIn->pfnEntry(DBGFPLUGINOP_INIT, pUVM, VMMR3GetVTable(), VBOX_VERSION);
371 if (RT_SUCCESS(rc))
372 {
373 /*
374 * Link it and we're good.
375 */
376 pPlugIn->pNext = pUVM->dbgf.s.pPlugInHead;
377 pUVM->dbgf.s.pPlugInHead = pPlugIn;
378
379 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
380 return VINF_SUCCESS;
381 }
382
383 RTErrInfoSet(pErrInfo, rc, "Plug-in init failed");
384 LogRel(("DBGF: Plug-in '%s' failed during init: %Rrc\n", pPlugIn->szName, rc));
385 RTLdrClose(pPlugIn->hLdrMod);
386 }
387 MMR3HeapFree(pPlugIn);
388
389 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
390 return rc;
391}
392
393
394/**
395 * Load a debugging plug-in.
396 *
397 * @returns VBox status code.
398 * @retval VERR_ALREADY_EXISTS if the module was already loaded.
399 * @retval VINF_BUFFER_OVERFLOW if the actual plug-in name buffer was too small
400 * (the plug-in was still successfully loaded).
401 * @param pUVM Pointer to the user-mode VM structure.
402 * @param pszPlugIn The plug-in name. This may specify the exact path to
403 * the plug-in module, or it may just specify the core name
404 * of the plug-in without prefix, suffix and path.
405 * @param pszActual Buffer to return the actual plug-in name in. Optional.
406 * This will be returned on VERR_ALREADY_EXSIST too.
407 * @param cbActual The size of @a pszActual.
408 * @param pErrInfo Optional error information structure.
409 */
410VMMR3DECL(int) DBGFR3PlugInLoad(PUVM pUVM, const char *pszPlugIn, char *pszActual, size_t cbActual, PRTERRINFO pErrInfo)
411{
412 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
413 AssertPtrReturn(pszPlugIn, VERR_INVALID_PARAMETER);
414
415 /*
416 * Extract the plug-in name. Copy it to the return buffer as we'll want to
417 * return it in the VERR_ALREADY_EXISTS case too.
418 */
419 char szName[DBGFPLUGIN_MAX_NAME];
420 int rc = dbgfPlugInExtractName(szName, pszPlugIn, pErrInfo);
421 if (RT_SUCCESS(rc))
422 {
423 int rc2 = VINF_SUCCESS;
424 if (pszActual)
425 rc2 = RTStrCopy(pszActual, cbActual, szName);
426
427 /*
428 * Write lock releated DBGF bits and try load it.
429 */
430 rc = VMR3ReqPriorityCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)dbgfR3PlugInLoad, 4, pUVM, szName, pszPlugIn, pErrInfo);
431 if (rc2 != VINF_SUCCESS && RT_SUCCESS(rc))
432 rc = VINF_BUFFER_OVERFLOW;
433 }
434
435 return rc;
436}
437
438
439/**
440 * Load all plug-ins from the architechture private directory of VBox.
441 *
442 * @param pUVM Pointer to the user-mode VM structure.
443 */
444VMMR3DECL(void) DBGFR3PlugInLoadAll(PUVM pUVM)
445{
446 UVM_ASSERT_VALID_EXT_RETURN_VOID(pUVM);
447
448 /*
449 * Pass it on to EMT(0) if necessary (thanks to DBGFR3Os*).
450 */
451 if (VMR3GetVMCPUId(pUVM->pVM) != 0)
452 {
453 VMR3ReqPriorityCallVoidWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)DBGFR3PlugInLoadAll, 1, pUVM);
454 return;
455 }
456
457
458 /*
459 * Open the architecture specific directory with a filter on our prefix
460 * and names including a dot.
461 */
462 const char *pszSuff = RTLdrGetSuff();
463 size_t cchSuff = strlen(pszSuff);
464
465 char szPath[RTPATH_MAX];
466 int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - cchSuff);
467 AssertRCReturnVoid(rc);
468 size_t offDir = strlen(szPath);
469
470 rc = RTPathAppend(szPath, sizeof(szPath) - cchSuff, DBGF_PLUG_IN_PREFIX "*");
471 AssertRCReturnVoid(rc);
472 strcat(szPath, pszSuff);
473
474 RTDIR hDir;
475 rc = RTDirOpenFiltered(&hDir, szPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
476 if (RT_SUCCESS(rc))
477 {
478 /*
479 * Now read it and try load each of the plug-in modules.
480 */
481 RTDIRENTRY DirEntry;
482 while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, NULL)))
483 {
484 szPath[offDir] = '\0';
485 rc = RTPathAppend(szPath, sizeof(szPath), DirEntry.szName);
486 if (RT_SUCCESS(rc))
487 {
488 char szName[DBGFPLUGIN_MAX_NAME];
489 rc = dbgfPlugInExtractName(szName, DirEntry.szName, NULL);
490 if (RT_SUCCESS(rc))
491 {
492 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
493 dbgfR3PlugInLoad(pUVM, szName, szPath, NULL);
494 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
495 }
496 }
497 }
498
499 RTDirClose(hDir);
500 }
501}
502
503
504/**
505 * Unloads a plug-in by name (no path, prefix or suffix).
506 *
507 * @returns VBox status code.
508 * @retval VERR_NOT_FOUND if the specified plug-in wasn't found.
509 * @param pUVM Pointer to the user-mode VM structure.
510 * @param pszName The name of the plug-in to unload.
511 */
512VMMR3DECL(int) DBGFR3PlugInUnload(PUVM pUVM, const char *pszName)
513{
514 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
515
516 /*
517 * Pass it on to EMT(0) if necessary (thanks to DBGFR3Os*).
518 */
519 if (VMR3GetVMCPUId(pUVM->pVM) != 0)
520 return VMR3ReqPriorityCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)DBGFR3PlugInUnload, 2, pUVM, pszName);
521
522
523 /*
524 * Find the plug-in.
525 */
526 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
527
528 int rc;
529 PDBGFPLUGIN pPrevPlugIn;
530 PDBGFPLUGIN pPlugIn = dbgfR3PlugInLocate(pUVM, pszName, &pPrevPlugIn);
531 if (pPlugIn)
532 {
533 /*
534 * Unlink, terminate, unload and free the plug-in.
535 */
536 if (pPrevPlugIn)
537 pPrevPlugIn->pNext = pPlugIn->pNext;
538 else
539 pUVM->dbgf.s.pPlugInHead = pPlugIn->pNext;
540
541 pPlugIn->pfnEntry(DBGFPLUGINOP_TERM, pUVM, VMMR3GetVTable(), 0);
542 RTLdrClose(pPlugIn->hLdrMod);
543
544 pPlugIn->pfnEntry = NULL;
545 pPlugIn->hLdrMod = NIL_RTLDRMOD;
546 MMR3HeapFree(pPlugIn->pNext);
547 rc = VINF_SUCCESS;
548 }
549 else
550 rc = VERR_NOT_FOUND;
551
552 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
553 return rc;
554}
555
556
557/**
558 * Unload all plug-ins.
559 *
560 * @param pUVM Pointer to the user-mode VM structure.
561 */
562static DECLCALLBACK(void) dbgfPlugInUnloadAll(PUVM pUVM)
563{
564 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
565
566 while (pUVM->dbgf.s.pPlugInHead)
567 {
568 PDBGFPLUGIN pPlugin = pUVM->dbgf.s.pPlugInHead;
569 pUVM->dbgf.s.pPlugInHead = pPlugin->pNext;
570
571 pPlugin->pfnEntry(DBGFPLUGINOP_TERM, pUVM, VMMR3GetVTable(), 0);
572
573 int rc2 = RTLdrClose(pPlugin->hLdrMod);
574 AssertRC(rc2);
575
576 pPlugin->pfnEntry = NULL;
577 pPlugin->hLdrMod = NIL_RTLDRMOD;
578 MMR3HeapFree(pPlugin);
579 }
580
581 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
582}
583
584
585/**
586 * Unloads all plug-ins.
587 *
588 * @param pUVM Pointer to the user-mode VM structure.
589 */
590VMMR3DECL(void) DBGFR3PlugInUnloadAll(PUVM pUVM)
591{
592 UVM_ASSERT_VALID_EXT_RETURN_VOID(pUVM);
593 /* Thanks to DBGFR3Os, this must be done on EMT(0). */
594 VMR3ReqPriorityCallVoidWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)dbgfPlugInUnloadAll, 1, pUVM);
595}
596
597
598
599/**
600 * @callback_method_impl{FNDBGFHANDLERINT, The 'plugins' info item.}
601 */
602static DECLCALLBACK(void) dbgfR3PlugInInfoList(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
603{
604 PDBGFPLUGIN pPlugIn = pVM->pUVM->dbgf.s.pPlugInHead;
605 RT_NOREF_PV(pszArgs);
606 if (pPlugIn)
607 {
608 pHlp->pfnPrintf(pHlp, "Debugging plug-in%s: %s", pPlugIn->pNext ? "s" : "", pPlugIn->szName);
609 while ((pPlugIn = pPlugIn->pNext) != NULL)
610 pHlp->pfnPrintf(pHlp, ", %s", pPlugIn->szName);
611 pHlp->pfnPrintf(pHlp, "\n");
612
613 }
614 else
615 pHlp->pfnPrintf(pHlp, "No plug-ins loaded\n");
616}
617
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