VirtualBox

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

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

tstVBoxMultipleVM.cpp: Missed a few tabs.

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