VirtualBox

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

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

Frontends + Main: Adjust to the new rules wrt. to rc -> hrc,vrc usage. This also fixes quite a few bugs wrt shadow variables, wrong return values and output error translations / exit codes. Also see @todos added [more stuff, build fix] ​bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.0 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 hrc = 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 hrc;
103 ComPtr<IProgress> progress;
104 ComPtr<IMachine> machine;
105 Bstr machineName;
106
107 hrc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
108 if(SUCCEEDED(hrc))
109 hrc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
110 if(SUCCEEDED(hrc))
111 {
112 hrc = machine->LaunchVMProcess(pSession, Bstr("headless").raw(),
113 ComSafeArrayNullInParam(), progress.asOutParam());
114 }
115 if (SUCCEEDED(hrc) && !progress.isNull())
116 {
117 CHECK_ERROR_L(progress, WaitForCompletion(-1));
118 if (SUCCEEDED(hrc))
119 {
120 BOOL completed = true;
121 CHECK_ERROR_L(progress, COMGETTER(Completed)(&completed));
122 if (SUCCEEDED(hrc))
123 {
124 Assert(completed);
125 LONG iRc;
126 CHECK_ERROR_L(progress, COMGETTER(ResultCode)(&iRc));
127 if (SUCCEEDED(hrc))
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 hrc;
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, /** Cipher */
330 NULL, /** Password id */
331 NULL, /** Password */
332 NULL, /* Create flags */
333 ptrMachine.asOutParam()));
334 if (SUCCEEDED(rc))
335 {
336 CHECK_ERROR_L(pVBox, RegisterMachine(ptrMachine));
337 RTPrintf("Machine '%ls' created\n", machineName.raw());
338 }
339
340 RTSemEventSignal(g_PingEevent);
341 RTThreadSleep(100);
342 }
343 return rc;
344}
345
346
347static int tstClean(IVirtualBox *pVBox, IVirtualBoxClient *pClient)
348{
349 NOREF(pClient);
350 HRESULT rc = S_OK;
351
352 // stop all machines created for the test
353 for (uint32_t i = 0; i < g_Args.numberMachines; i++)
354 {
355 ComPtr<IMachine> machine;
356 ComPtr<IProgress> progress;
357 ComPtr<ISession> session;
358 SafeIfaceArray<IMedium> media;
359
360 Bstr machineName(tstMakeMachineName(i));
361
362 /* Delete created VM and its files */
363 CHECK_ERROR_L(pVBox, FindMachine(machineName.raw(), machine.asOutParam()));
364
365 // try to stop it again if it was not stopped
366 if (SUCCEEDED(rc))
367 {
368 MachineState_T machineState;
369 CHECK_ERROR_L(machine, COMGETTER(State)(&machineState));
370 if ( SUCCEEDED(rc)
371 && ( machineState == MachineState_Running
372 || machineState == MachineState_Paused) )
373 {
374 rc = session.createInprocObject(CLSID_Session);
375 if (SUCCEEDED(rc))
376 tstStopVM(pVBox, session, machineName, FALSE);
377 }
378 }
379
380 if (SUCCEEDED(rc))
381 CHECK_ERROR_L(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media)));
382 if (SUCCEEDED(rc))
383 CHECK_ERROR_L(machine, DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam()));
384 if (SUCCEEDED(rc))
385 CHECK_ERROR_L(progress, WaitForCompletion(-1));
386 if (SUCCEEDED(rc))
387 RTPrintf("Machine '%ls' deleted.\n", machineName.raw());
388 }
389 return rc;
390}
391
392
393static DECLCALLBACK(int) tstThreadRun(RTTHREAD hThreadSelf, void *pvUser)
394{
395 RT_NOREF(hThreadSelf);
396 TestThreadArgs* args = (TestThreadArgs*)pvUser;
397 Assert(args != NULL);
398 uint32_t maxPackSize = args->machinesPackSize;
399 uint32_t percentage = args->percentsUnlok;
400
401 HRESULT rc = com::Initialize();
402 if (SUCCEEDED(rc))
403 {
404 ComPtr<IVirtualBoxClient> ptrVBoxClient;
405 ComPtr<IVirtualBox> ptrVBox;
406
407 rc = TST_COM_EXPR(ptrVBoxClient.createInprocObject(CLSID_VirtualBoxClient));
408 if (SUCCEEDED(rc))
409 rc = TST_COM_EXPR(ptrVBoxClient->COMGETTER(VirtualBox)(ptrVBox.asOutParam()));
410 if (SUCCEEDED(rc))
411 {
412 RTPrintf("Creating machines...\n");
413 tstCreateMachines(ptrVBox);
414
415 while (g_RunTest)
416 {
417 rc = tstMachinesPack(ptrVBox, maxPackSize, percentage);
418 }
419
420 RTPrintf("Deleting machines...\n");
421 tstClean(ptrVBox, ptrVBoxClient);
422 }
423
424 g_RunTest = false;
425 RTSemEventSignal(g_PingEevent);
426 RTThreadSleep(100);
427
428 ptrVBox = NULL;
429 ptrVBoxClient = NULL;
430 com::Shutdown();
431 }
432 return rc;
433}
434
435
436static int ParseArguments(int argc, char **argv, TestThreadArgs *pArgs)
437{
438 RTGETOPTSTATE GetState;
439 RTGETOPTUNION ValueUnion;
440 static const RTGETOPTDEF s_aOptions[] =
441 {
442 { "--packsize", 'p', RTGETOPT_REQ_UINT32 }, // number of machines to start together
443 { "--lock", 's', RTGETOPT_REQ_UINT32 }, // percentage of VM sessions closed without Unlok
444 { "--time", 't', RTGETOPT_REQ_UINT64 }, // required time of load test execution, in seconds
445 { "--machines" , 'u', RTGETOPT_REQ_UINT32 }
446 };
447 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
448 AssertRCReturn(rc, rc);
449 AssertPtr(pArgs);
450
451 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
452 {
453 switch (rc)
454 {
455 case 'p':
456 if (ValueUnion.u32 == 0)
457 {
458 RTPrintf("--packsize should be more then zero\n");
459 return VERR_INVALID_PARAMETER;
460 }
461 if (ValueUnion.u32 > 16000)
462 {
463 RTPrintf("maximum --packsize value is 16000.\n"
464 "That means can use no more then 16000 machines for the test.\n");
465 return VERR_INVALID_PARAMETER;
466 }
467 pArgs->machinesPackSize = ValueUnion.u32;
468 break;
469
470 case 's':
471 if (ValueUnion.u32 > 100)
472 {
473 RTPrintf("maximum --lock value is 100.\n"
474 "That means 100 percent of sessions should be closed without unlock.\n");
475 return VERR_INVALID_PARAMETER;
476 }
477 pArgs->percentsUnlok = ValueUnion.u32;
478 break;
479
480 case 't':
481 pArgs->cMsExecutionTime = ValueUnion.u64 * 1000;
482 break;
483
484 case 'u':
485 if (ValueUnion.u32 > 16000)
486 {
487 RTPrintf("maximum --machines value is 16000.\n"
488 "That means can make no more then 16000 machines for the test.\n");
489 return VERR_INVALID_PARAMETER;
490 }
491 if (ValueUnion.u32 < pArgs->machinesPackSize)
492 {
493 RTPrintf("--machines value should be larger then --packsize value.\n");
494 return VERR_INVALID_PARAMETER;
495 }
496 pArgs->numberMachines = ValueUnion.u32;
497 break;
498
499 default:
500 RTGetOptPrintError(rc, &ValueUnion);
501 return rc;
502 }
503 }
504 return rc;
505}
506
507#endif /* RT_ARCH_AMD64 */
508
509
510/**
511 *
512 * Examples:
513 * - tstVBoxClientWatcherLoad --packsize 500 --lock 10 --time 14400 --machines 4000
514 * It will create 4000 VMs with names "utmvm0"..."utmvm3999". It will start
515 * 500 random VMs together, stop them, without closing their session with
516 * probability 10%, will repeat this over 4 hours. After test it will
517 * delete all "utmvm..." machines.
518 *
519 * - tstVBoxClientWatcherLoad --packsize 1 --lock 30 --time 3600 --machines 1000
520 * It will create 1000 VMs with names "utmvm0"..."utmvm999". It will start
521 * random VM - stop them, without closing their session with probability
522 * 30%, will repeat this over 30 minutes. After test it will delete all
523 * "utmvm..." machines.
524 */
525int main(int argc, char **argv)
526{
527 RT_NOREF(argc, argv);
528 RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxMultipleVM", &g_hTest);
529 if (rcExit != RTEXITCODE_SUCCESS)
530 return rcExit;
531 SUPR3Init(NULL);
532 com::Initialize();
533 RTTestBanner(g_hTest);
534
535#ifndef RT_ARCH_AMD64
536 /*
537 * Linux OOM killer when running many VMs on a 32-bit host.
538 */
539 return RTTestSkipAndDestroy(g_hTest, "The test can only run reliably on 64-bit hosts.");
540#else /* RT_ARCH_AMD64 */
541
542 RTPrintf("Initializing ...\n");
543 int rc = RTSemEventCreate(&g_PingEevent);
544 AssertRC(rc);
545
546 g_Args.machinesPackSize = 100;
547 g_Args.percentsUnlok = 10;
548 g_Args.cMsExecutionTime = 3*RT_MS_1MIN;
549 g_Args.numberMachines = 200;
550
551 /*
552 * Skip this test for the time being. Saw crashes on several test boxes but no time
553 * to debug.
554 */
555 if (argc == 1)
556 return RTTestSkipAndDestroy(g_hTest, "Test crashes sometimes.\n");
557
558 rc = ParseArguments(argc, argv, &g_Args);
559 if (RT_FAILURE(rc))
560 return RTTestSkipAndDestroy(g_hTest, "Invalid arguments.\n");
561
562 RTPrintf("Arguments packSize = %d, percentUnlok = %d, time = %lld.\n",
563 g_Args.machinesPackSize, g_Args.percentsUnlok, g_Args.cMsExecutionTime);
564
565 RTTHREAD hThread;
566 rc = RTThreadCreate(&hThread, tstThreadRun, (void *)&g_Args,
567 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tstThreadRun");
568 if (RT_SUCCESS(rc))
569 {
570 AssertRC(rc);
571
572 uint64_t msStart = RTTimeMilliTS();
573 while (RTTimeMilliTS() - msStart < g_Args.cMsExecutionTime && g_RunTest)
574 {
575 // check that test thread didn't hang and call us periodically
576 // allowed 30 seconds for operation - msStart or stop VM
577 rc = RTSemEventWait(g_PingEevent, 3 * 60 * 1000);
578 if (RT_FAILURE(rc))
579 {
580 if (rc == VERR_TIMEOUT)
581 {
582 RTTestFailed(g_hTest, "Timeout. Deadlock?\n");
583 com::Shutdown();
584 return RTTestSummaryAndDestroy(g_hTest);
585 }
586 AssertRC(rc);
587 }
588 }
589
590 RTPrintf("Finishing...\n");
591
592 // finish test thread
593 g_RunTest = false;
594 // wait it for finish
595 RTThreadWait(hThread, RT_INDEFINITE_WAIT, &rc);
596 }
597 RTSemEventDestroy(g_PingEevent);
598
599 com::Shutdown();
600 if (RT_FAILURE(rc))
601 RTTestFailed(g_hTest, "Test failed.\n");
602 else
603 RTTestPassed(g_hTest, "Test finished.\n");
604 return RTTestSummaryAndDestroy(g_hTest);
605#endif /* RT_ARCH_AMD64 */
606}
607
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