VirtualBox

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

Last change on this file since 63177 was 63177, checked in by vboxsync, 8 years ago

Main: warnings

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