VirtualBox

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

Last change on this file since 61644 was 61205, checked in by vboxsync, 9 years ago

sorry

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