VirtualBox

source: vbox/trunk/src/VBox/Main/testcase/tstVBoxMultipleVM.cpp@ 94267

Last change on this file since 94267 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: 20.8 KB
Line 
1/** @file
2 * tstVBoxMultipleVM - load test for ClientWatcher.
3 */
4
5/*
6 * Copyright (C) 2006-2022 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#include <VBox/com/com.h>
22#include <VBox/com/string.h>
23#include <VBox/com/array.h>
24#include <VBox/com/Guid.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <iprt/assert.h>
28#include <iprt/errcore.h>
29#include <VBox/com/VirtualBox.h>
30#include <iprt/stream.h>
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <VBox/sup.h>
34
35#include <vector>
36#include <algorithm>
37
38#include <iprt/test.h>
39#include <iprt/time.h>
40#include <iprt/rand.h>
41#include <iprt/getopt.h>
42
43using namespace com;
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/* Arguments of test thread */
50struct TestThreadArgs
51{
52 /** number of machines that should be run simultaneousely */
53 uint32_t machinesPackSize;
54 /** percents of VM Stop operation what should be called
55 * without session unlocking */
56 uint32_t percentsUnlok;
57 /** How much time in milliseconds test will be executed */
58 uint64_t cMsExecutionTime;
59 /** How much machines create for the test */
60 uint32_t numberMachines;
61};
62
63
64/*********************************************************************************************************************************
65* Global Variables & defs *
66*********************************************************************************************************************************/
67static RTTEST g_hTest;
68#ifdef RT_ARCH_AMD64
69typedef std::vector<Bstr> TMachinesList;
70static volatile bool g_RunTest = true;
71static RTSEMEVENT g_PingEevent;
72static volatile uint64_t g_Counter = 0;
73static TestThreadArgs g_Args;
74
75
76/** Worker for TST_COM_EXPR(). */
77static HRESULT tstComExpr(HRESULT hrc, const char *pszOperation, int iLine)
78{
79 if (FAILED(hrc))
80 {
81 RTTestFailed(g_hTest, "%s failed on line %u with hrc=%Rhrc\n", pszOperation, iLine, hrc);
82 }
83 return hrc;
84}
85
86
87#define CHECK_ERROR_L(iface, method) \
88 do { \
89 rc = iface->method; \
90 if (FAILED(rc)) \
91 RTPrintf("warning: %s->%s failed on line %u with hrc=%Rhrc\n", #iface, #method, __LINE__, rc);\
92 } while (0)
93
94
95/** Macro that executes the given expression and report any failure.
96 * The expression must return a HRESULT. */
97#define TST_COM_EXPR(expr) tstComExpr(expr, #expr, __LINE__)
98
99
100static int tstStartVM(IVirtualBox *pVBox, ISession *pSession, Bstr machineID, bool fSkipUnlock)
101{
102 HRESULT rc;
103 ComPtr<IProgress> progress;
104 ComPtr<IMachine> machine;
105 Bstr machineName;
106
107 rc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
108 if(SUCCEEDED(rc))
109 rc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
110 if(SUCCEEDED(rc))
111 {
112 rc = machine->LaunchVMProcess(pSession, Bstr("headless").raw(),
113 ComSafeArrayNullInParam(), progress.asOutParam());
114 }
115 if (SUCCEEDED(rc) && !progress.isNull())
116 {
117 CHECK_ERROR_L(progress, WaitForCompletion(-1));
118 if (SUCCEEDED(rc))
119 {
120 BOOL completed = true;
121 CHECK_ERROR_L(progress, COMGETTER(Completed)(&completed));
122 if (SUCCEEDED(rc))
123 {
124 Assert(completed);
125 LONG iRc;
126 CHECK_ERROR_L(progress, COMGETTER(ResultCode)(&iRc));
127 if (SUCCEEDED(rc))
128 {
129 if (FAILED(iRc))
130 {
131 ProgressErrorInfo info(progress);
132 RTPrintf("Start VM '%ls' failed. Warning: %ls.\n", machineName.raw(), info.getText().raw());
133 }
134 else
135 RTPrintf("VM '%ls' started.\n", machineName.raw());
136 }
137 }
138 }
139 if (!fSkipUnlock)
140 pSession->UnlockMachine();
141 else
142 RTPrintf("Session unlock skipped.\n");
143 }
144 return rc;
145}
146
147
148static int tstStopVM(IVirtualBox* pVBox, ISession* pSession, Bstr machineID, bool fSkipUnlock)
149{
150 ComPtr<IMachine> machine;
151 HRESULT rc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
152 if (SUCCEEDED(rc))
153 {
154 Bstr machineName;
155 rc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
156 if (SUCCEEDED(rc))
157 {
158 MachineState_T machineState;
159 rc = TST_COM_EXPR(machine->COMGETTER(State)(&machineState));
160 // check that machine is in running state
161 if ( SUCCEEDED(rc)
162 && ( machineState == MachineState_Running
163 || machineState == MachineState_Paused))
164 {
165 ComPtr<IConsole> console;
166 ComPtr<IProgress> progress;
167
168 rc = TST_COM_EXPR(machine->LockMachine(pSession, LockType_Shared));
169 if(SUCCEEDED(rc))
170 TST_COM_EXPR(pSession->COMGETTER(Console)(console.asOutParam()));
171 if(SUCCEEDED(rc))
172 rc = console->PowerDown(progress.asOutParam());
173 if (SUCCEEDED(rc) && !progress.isNull())
174 {
175 //RTPrintf("Stopping VM %ls...\n", machineName.raw());
176 CHECK_ERROR_L(progress, WaitForCompletion(-1));
177 if (SUCCEEDED(rc))
178 {
179 BOOL completed = true;
180 CHECK_ERROR_L(progress, COMGETTER(Completed)(&completed));
181 if (SUCCEEDED(rc))
182 {
183 //ASSERT(completed);
184 LONG iRc;
185 CHECK_ERROR_L(progress, COMGETTER(ResultCode)(&iRc));
186 if (SUCCEEDED(rc))
187 {
188 if (FAILED(iRc))
189 {
190 ProgressErrorInfo info(progress);
191 RTPrintf("Stop VM %ls failed. Warning: %ls.\n", machineName.raw(), info.getText().raw());
192 rc = iRc;
193 }
194 else
195 {
196 RTPrintf("VM '%ls' stopped.\n", machineName.raw());
197 }
198 }
199 }
200 }
201 if (!fSkipUnlock)
202 pSession->UnlockMachine();
203 else
204 RTPrintf("Session unlock skipped.\n");
205 }
206 }
207 }
208 }
209 return rc;
210}
211
212
213/**
214 * Get random @a maxCount machines from list of existing VMs.
215 *
216 * @note Can return less then maxCount machines.
217 */
218static int tstGetMachinesList(IVirtualBox *pVBox, uint32_t maxCount, TMachinesList &listToFill)
219{
220 com::SafeIfaceArray<IMachine> machines;
221 HRESULT rc = TST_COM_EXPR(pVBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)));
222 if (SUCCEEDED(rc))
223 {
224
225 size_t cMachines = RT_MIN(machines.size(), maxCount);
226 for (size_t i = 0; i < cMachines; ++i)
227 {
228 // choose random index of machine
229 uint32_t idx = RTRandU32Ex(0, (uint32_t)machines.size() - 1);
230 if (machines[idx])
231 {
232 Bstr bstrId;
233 Bstr machineName;
234 CHECK_ERROR_L(machines[idx], COMGETTER(Id)(bstrId.asOutParam()));
235 if (SUCCEEDED(rc))
236 CHECK_ERROR_L(machines[idx], COMGETTER(Name)(machineName.asOutParam()));
237 if (SUCCEEDED(rc))
238 {
239 if (Utf8Str(machineName).startsWith("umtvm"))
240 listToFill.push_back(bstrId);
241 }
242 }
243 }
244
245 // remove duplicates from the vector
246 std::sort(listToFill.begin(), listToFill.end());
247 listToFill.erase(std::unique(listToFill.begin(), listToFill.end()), listToFill.end());
248 RTPrintf("Filled pack of %d from %d machines.\n", listToFill.size(), machines.size());
249 }
250
251 return rc;
252}
253
254
255static int tstMachinesPack(IVirtualBox *pVBox, uint32_t maxPackSize, uint32_t percentage)
256{
257 HRESULT rc = S_OK;
258 TMachinesList machinesList;
259 bool alwaysUnlock = false;
260 uint64_t percN = 0;
261
262 // choose and fill pack of machines for test
263 tstGetMachinesList(pVBox, maxPackSize, machinesList);
264
265 RTPrintf("Start test.\n");
266 // screw up counter
267 g_Counter = UINT64_MAX - machinesList.size() <= g_Counter ? 0 : g_Counter;
268 if (percentage > 0)
269 percN = 100 / percentage;
270 else
271 alwaysUnlock = true;
272
273 // start all machines in pack
274 for (TMachinesList::iterator it = machinesList.begin();
275 it != machinesList.end() && g_RunTest;
276 ++it)
277 {
278 ComPtr<ISession> session;
279 rc = session.createInprocObject(CLSID_Session);
280 if (SUCCEEDED(rc))
281 {
282 rc = tstStartVM(pVBox, session, *it, !(alwaysUnlock || g_Counter++ % percN));
283 }
284 RTSemEventSignal(g_PingEevent);
285 RTThreadSleep(100);
286 }
287 // stop all machines in the pack
288 for (TMachinesList::iterator it = machinesList.begin();
289 it != machinesList.end() && g_RunTest;
290 ++it)
291 {
292 ComPtr<ISession> session;
293 rc = session.createInprocObject(CLSID_Session);
294 if (SUCCEEDED(rc))
295 {
296 // stop machines, skip session unlock of given % of machines
297 rc = tstStopVM(pVBox, session, *it, !(alwaysUnlock || g_Counter++ % percN));
298 }
299 RTSemEventSignal(g_PingEevent);
300 RTThreadSleep(100);
301 }
302 return rc;
303}
304
305
306static Bstr tstMakeMachineName(int i)
307{
308 char szMachineName[32];
309 RTStrPrintf(szMachineName, sizeof(szMachineName), "umtvm%d", i);
310 return Bstr(szMachineName);
311}
312
313
314static int tstCreateMachines(IVirtualBox *pVBox)
315{
316 HRESULT rc = S_OK;
317 // create machines for the test
318 for (uint32_t i = 0; i < g_Args.numberMachines; i++)
319 {
320 ComPtr<IMachine> ptrMachine;
321 com::SafeArray<BSTR> groups;
322
323 Bstr machineName(tstMakeMachineName(i));
324 /* Default VM settings */
325 CHECK_ERROR_L(pVBox, CreateMachine(NULL, /* Settings */
326 machineName.raw(), /* Name */
327 ComSafeArrayAsInParam(groups), /* Groups */
328 NULL, /* OS Type */
329 NULL, /* Create flags */
330 ptrMachine.asOutParam()));
331 if (SUCCEEDED(rc))
332 {
333 CHECK_ERROR_L(pVBox, RegisterMachine(ptrMachine));
334 RTPrintf("Machine '%ls' created\n", machineName.raw());
335 }
336
337 RTSemEventSignal(g_PingEevent);
338 RTThreadSleep(100);
339 }
340 return rc;
341}
342
343
344static int tstClean(IVirtualBox *pVBox, IVirtualBoxClient *pClient)
345{
346 NOREF(pClient);
347 HRESULT rc = S_OK;
348
349 // stop all machines created for the test
350 for (uint32_t i = 0; i < g_Args.numberMachines; i++)
351 {
352 ComPtr<IMachine> machine;
353 ComPtr<IProgress> progress;
354 ComPtr<ISession> session;
355 SafeIfaceArray<IMedium> media;
356
357 Bstr machineName(tstMakeMachineName(i));
358
359 /* Delete created VM and its files */
360 CHECK_ERROR_L(pVBox, FindMachine(machineName.raw(), machine.asOutParam()));
361
362 // try to stop it again if it was not stopped
363 if (SUCCEEDED(rc))
364 {
365 MachineState_T machineState;
366 CHECK_ERROR_L(machine, COMGETTER(State)(&machineState));
367 if ( SUCCEEDED(rc)
368 && ( machineState == MachineState_Running
369 || machineState == MachineState_Paused) )
370 {
371 rc = session.createInprocObject(CLSID_Session);
372 if (SUCCEEDED(rc))
373 tstStopVM(pVBox, session, machineName, FALSE);
374 }
375 }
376
377 if (SUCCEEDED(rc))
378 CHECK_ERROR_L(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media)));
379 if (SUCCEEDED(rc))
380 CHECK_ERROR_L(machine, DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam()));
381 if (SUCCEEDED(rc))
382 CHECK_ERROR_L(progress, WaitForCompletion(-1));
383 if (SUCCEEDED(rc))
384 RTPrintf("Machine '%ls' deleted.\n", machineName.raw());
385 }
386 return rc;
387}
388
389
390static DECLCALLBACK(int) tstThreadRun(RTTHREAD hThreadSelf, void *pvUser)
391{
392 RT_NOREF(hThreadSelf);
393 TestThreadArgs* args = (TestThreadArgs*)pvUser;
394 Assert(args != NULL);
395 uint32_t maxPackSize = args->machinesPackSize;
396 uint32_t percentage = args->percentsUnlok;
397
398 HRESULT rc = com::Initialize();
399 if (SUCCEEDED(rc))
400 {
401 ComPtr<IVirtualBoxClient> ptrVBoxClient;
402 ComPtr<IVirtualBox> ptrVBox;
403
404 rc = TST_COM_EXPR(ptrVBoxClient.createInprocObject(CLSID_VirtualBoxClient));
405 if (SUCCEEDED(rc))
406 rc = TST_COM_EXPR(ptrVBoxClient->COMGETTER(VirtualBox)(ptrVBox.asOutParam()));
407 if (SUCCEEDED(rc))
408 {
409 RTPrintf("Creating machines...\n");
410 tstCreateMachines(ptrVBox);
411
412 while (g_RunTest)
413 {
414 rc = tstMachinesPack(ptrVBox, maxPackSize, percentage);
415 }
416
417 RTPrintf("Deleting machines...\n");
418 tstClean(ptrVBox, ptrVBoxClient);
419 }
420
421 g_RunTest = false;
422 RTSemEventSignal(g_PingEevent);
423 RTThreadSleep(100);
424
425 ptrVBox = NULL;
426 ptrVBoxClient = NULL;
427 com::Shutdown();
428 }
429 return rc;
430}
431
432
433static int ParseArguments(int argc, char **argv, TestThreadArgs *pArgs)
434{
435 RTGETOPTSTATE GetState;
436 RTGETOPTUNION ValueUnion;
437 static const RTGETOPTDEF s_aOptions[] =
438 {
439 { "--packsize", 'p', RTGETOPT_REQ_UINT32 }, // number of machines to start together
440 { "--lock", 's', RTGETOPT_REQ_UINT32 }, // percentage of VM sessions closed without Unlok
441 { "--time", 't', RTGETOPT_REQ_UINT64 }, // required time of load test execution, in seconds
442 { "--machines" , 'u', RTGETOPT_REQ_UINT32 }
443 };
444 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
445 AssertRCReturn(rc, rc);
446 AssertPtr(pArgs);
447
448 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
449 {
450 switch (rc)
451 {
452 case 'p':
453 if (ValueUnion.u32 == 0)
454 {
455 RTPrintf("--packsize should be more then zero\n");
456 return VERR_INVALID_PARAMETER;
457 }
458 if (ValueUnion.u32 > 16000)
459 {
460 RTPrintf("maximum --packsize value is 16000.\n"
461 "That means can use no more then 16000 machines for the test.\n");
462 return VERR_INVALID_PARAMETER;
463 }
464 pArgs->machinesPackSize = ValueUnion.u32;
465 break;
466
467 case 's':
468 if (ValueUnion.u32 > 100)
469 {
470 RTPrintf("maximum --lock value is 100.\n"
471 "That means 100 percent of sessions should be closed without unlock.\n");
472 return VERR_INVALID_PARAMETER;
473 }
474 pArgs->percentsUnlok = ValueUnion.u32;
475 break;
476
477 case 't':
478 pArgs->cMsExecutionTime = ValueUnion.u64 * 1000;
479 break;
480
481 case 'u':
482 if (ValueUnion.u32 > 16000)
483 {
484 RTPrintf("maximum --machines value is 16000.\n"
485 "That means can make no more then 16000 machines for the test.\n");
486 return VERR_INVALID_PARAMETER;
487 }
488 if (ValueUnion.u32 < pArgs->machinesPackSize)
489 {
490 RTPrintf("--machines value should be larger then --packsize value.\n");
491 return VERR_INVALID_PARAMETER;
492 }
493 pArgs->numberMachines = ValueUnion.u32;
494 break;
495
496 default:
497 RTGetOptPrintError(rc, &ValueUnion);
498 return rc;
499 }
500 }
501 return rc;
502}
503
504#endif /* RT_ARCH_AMD64 */
505
506
507/**
508 *
509 * Examples:
510 * - tstVBoxClientWatcherLoad --packsize 500 --lock 10 --time 14400 --machines 4000
511 * It will create 4000 VMs with names "utmvm0"..."utmvm3999". It will start
512 * 500 random VMs together, stop them, without closing their session with
513 * probability 10%, will repeat this over 4 hours. After test it will
514 * delete all "utmvm..." machines.
515 *
516 * - tstVBoxClientWatcherLoad --packsize 1 --lock 30 --time 3600 --machines 1000
517 * It will create 1000 VMs with names "utmvm0"..."utmvm999". It will start
518 * random VM - stop them, without closing their session with probability
519 * 30%, will repeat this over 30 minutes. After test it will delete all
520 * "utmvm..." machines.
521 */
522int main(int argc, char **argv)
523{
524 RT_NOREF(argc, argv);
525 RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxMultipleVM", &g_hTest);
526 if (rcExit != RTEXITCODE_SUCCESS)
527 return rcExit;
528 SUPR3Init(NULL);
529 com::Initialize();
530 RTTestBanner(g_hTest);
531
532#ifndef RT_ARCH_AMD64
533 /*
534 * Linux OOM killer when running many VMs on a 32-bit host.
535 */
536 return RTTestSkipAndDestroy(g_hTest, "The test can only run reliably on 64-bit hosts.");
537#else /* RT_ARCH_AMD64 */
538
539 RTPrintf("Initializing ...\n");
540 int rc = RTSemEventCreate(&g_PingEevent);
541 AssertRC(rc);
542
543 g_Args.machinesPackSize = 100;
544 g_Args.percentsUnlok = 10;
545 g_Args.cMsExecutionTime = 3*RT_MS_1MIN;
546 g_Args.numberMachines = 200;
547
548 /*
549 * Skip this test for the time being. Saw crashes on several test boxes but no time
550 * to debug.
551 */
552 if (argc == 1)
553 return RTTestSkipAndDestroy(g_hTest, "Test crashes sometimes.\n");
554
555 rc = ParseArguments(argc, argv, &g_Args);
556 if (RT_FAILURE(rc))
557 return RTTestSkipAndDestroy(g_hTest, "Invalid arguments.\n");
558
559 RTPrintf("Arguments packSize = %d, percentUnlok = %d, time = %lld.\n",
560 g_Args.machinesPackSize, g_Args.percentsUnlok, g_Args.cMsExecutionTime);
561
562 RTTHREAD hThread;
563 rc = RTThreadCreate(&hThread, tstThreadRun, (void *)&g_Args,
564 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tstThreadRun");
565 if (RT_SUCCESS(rc))
566 {
567 AssertRC(rc);
568
569 uint64_t msStart = RTTimeMilliTS();
570 while (RTTimeMilliTS() - msStart < g_Args.cMsExecutionTime && g_RunTest)
571 {
572 // check that test thread didn't hang and call us periodically
573 // allowed 30 seconds for operation - msStart or stop VM
574 rc = RTSemEventWait(g_PingEevent, 3 * 60 * 1000);
575 if (RT_FAILURE(rc))
576 {
577 if (rc == VERR_TIMEOUT)
578 {
579 RTTestFailed(g_hTest, "Timeout. Deadlock?\n");
580 com::Shutdown();
581 return RTTestSummaryAndDestroy(g_hTest);
582 }
583 AssertRC(rc);
584 }
585 }
586
587 RTPrintf("Finishing...\n");
588
589 // finish test thread
590 g_RunTest = false;
591 // wait it for finish
592 RTThreadWait(hThread, RT_INDEFINITE_WAIT, &rc);
593 }
594 RTSemEventDestroy(g_PingEevent);
595
596 com::Shutdown();
597 if (RT_FAILURE(rc))
598 RTTestFailed(g_hTest, "Test failed.\n");
599 else
600 RTTestPassed(g_hTest, "Test finished.\n");
601 return RTTestSummaryAndDestroy(g_hTest);
602#endif /* RT_ARCH_AMD64 */
603}
604
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