VirtualBox

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

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

wording

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