VirtualBox

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

Last change on this file since 75142 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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