VirtualBox

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use