VirtualBox

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

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

fixed properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 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;
53static Bstr tstMachineName = "tstVBoxMultipleVM test multiple VM start/stop";
54
55/* Arguments of test thread */
56struct TestThreadArgs
57{
58 /* number of machines that should be run simultaneousely */
59 uint32_t machinesPackSize;
60 /* percents of VM Stop operation what should be called */
61 /* without session unlocking */
62 uint32_t percentsUnlok;
63 /* How much time in seconds test will be executed */
64 uint64_t executionTime;
65 /* How much machines create for the test */
66 uint32_t numberMachines;
67};
68
69static TestThreadArgs g_Args;
70
71
72/** Worker for TST_COM_EXPR(). */
73static HRESULT tstComExpr(HRESULT hrc, const char *pszOperation, int iLine)
74{
75 if (FAILED(hrc))
76 {
77 RTTestFailed(g_hTest, "%s failed on line %u with hrc=%Rhrc\n", pszOperation, iLine, hrc);
78 }
79 return hrc;
80}
81
82
83/** Macro that executes the given expression and report any failure.
84 * The expression must return a HRESULT. */
85#define TST_COM_EXPR(expr) tstComExpr(expr, #expr, __LINE__)
86
87
88static int tstStartVM(IVirtualBox* pVBox, ISession* pSession, Bstr machineID, BOOL skipUnlok)
89{
90 HRESULT rc;
91 ComPtr<IProgress> progress;
92 ComPtr<IMachine> machine;
93 Bstr machineName;
94
95 rc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
96 if(SUCCEEDED(rc))
97 rc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
98 if(SUCCEEDED(rc))
99 rc = machine->LaunchVMProcess(pSession, Bstr("headless").raw(),
100 Bstr("").raw(), progress.asOutParam());
101 if (SUCCEEDED(rc) && !progress.isNull())
102 {
103 CHECK_ERROR(progress, WaitForCompletion(-1));
104 if (SUCCEEDED(rc))
105 {
106 BOOL completed = true;
107 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
108 if (SUCCEEDED(rc))
109 {
110 Assert(completed);
111 LONG iRc;
112 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
113 if (SUCCEEDED(rc))
114 {
115 if (FAILED(iRc))
116 {
117 ProgressErrorInfo info(progress);
118 RTPrintf("Start VM '%ls' failed.Error: %ls.\n", machineName.raw(), info.getText().raw());
119 }
120 else
121 {
122 RTPrintf("VM '%ls' started.\n", machineName.raw());
123 }
124 }
125 }
126 }
127 if (!skipUnlok)
128 {
129 pSession->UnlockMachine();
130 }
131 else
132 {
133 RTPrintf("Session unlock skipped.\n");
134 }
135 }
136 return rc;
137}
138
139
140static int tstStopVM(IVirtualBox* pVBox, ISession* pSession, Bstr machineID, BOOL skipUnlok)
141{
142 HRESULT rc;
143 MachineState_T machineState;
144 ComPtr<IMachine> machine;
145 Bstr machineName;
146
147 rc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
148 if(SUCCEEDED(rc))
149 rc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
150 if(SUCCEEDED(rc))
151 rc = TST_COM_EXPR(machine->COMGETTER(State)(&machineState));
152 // check that machine is in running state
153 if (machineState == MachineState_Running
154 || machineState == MachineState_Paused)
155 {
156 ComPtr<IConsole> console;
157 ComPtr<IProgress> progress;
158
159 rc = TST_COM_EXPR(machine->LockMachine(pSession, LockType_Shared));
160 if(SUCCEEDED(rc))
161 TST_COM_EXPR(pSession->COMGETTER(Console)(console.asOutParam()));
162 if(SUCCEEDED(rc))
163 rc = console->PowerDown(progress.asOutParam());
164 if (SUCCEEDED(rc) && !progress.isNull())
165 {
166 //RTPrintf("Stopping VM %ls...\n", machineName.raw());
167 CHECK_ERROR(progress, WaitForCompletion(-1));
168 if (SUCCEEDED(rc))
169 {
170 BOOL completed = true;
171 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
172 if (SUCCEEDED(rc))
173 {
174 //ASSERT(completed);
175 LONG iRc;
176 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
177 if (SUCCEEDED(rc))
178 {
179 if (FAILED(iRc))
180 {
181 ProgressErrorInfo info(progress);
182 RTPrintf("Stop VM %ls failed. Error: %ls.\n", machineName.raw(), info.getText().raw());
183 rc = iRc;
184 }
185 else
186 {
187 RTPrintf("VM '%ls' stopped.\n", machineName.raw());
188 }
189 }
190 }
191 }
192 if (!skipUnlok)
193 {
194 pSession->UnlockMachine();
195 }
196 else
197 {
198 RTPrintf("Session unlock skipped.\n");
199 }
200 }
201 }
202 return rc;
203}
204
205
206/**
207* Get random maxCount machines from list of existing VMs
208* Note: can return less then maxCount machines
209*/
210static int tstGetMachinesList(IVirtualBox *pVBox, uint32_t maxCount, TMachinesList& listToFill)
211{
212 HRESULT rc;
213 uint32_t machinesCount = 0;
214 com::SafeIfaceArray<IMachine> machines;
215
216 TST_COM_EXPR(pVBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)));
217
218 machinesCount = RT_MIN(machines.size(), maxCount);
219 for (uint32_t i = 0; i < machinesCount; ++i)
220 {
221 // choose random index of machine
222 uint32_t idx = RTRandU32Ex(0, machines.size() - 1);
223 if (machines[idx])
224 {
225 Bstr strId;
226 Bstr machineName;
227 CHECK_ERROR(machines[idx], COMGETTER(Id)(strId.asOutParam()));
228 if (SUCCEEDED(rc))
229 CHECK_ERROR(machines[idx], COMGETTER(Name)(machineName.asOutParam()));
230 if(SUCCEEDED(rc))
231 {
232 if(Utf8Str(machineName).startsWith("umtvm"))
233 listToFill.push_back(strId);
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 return rc;
244}
245
246
247static int tstMachinesPack(IVirtualBox *pVBox, uint32_t maxPackSize, uint32_t percentage)
248{
249 HRESULT rc = S_OK;
250 TMachinesList machinesList;
251 bool alwaysUnlock = false;
252 uint64_t percN = 0;
253
254 // choose and fill pack of machines for test
255 tstGetMachinesList(pVBox, maxPackSize, machinesList);
256
257 RTPrintf("Start test.\n");
258 // screw up counter
259 g_Counter = UINT64_MAX - machinesList.size() <= g_Counter ? 0 : g_Counter;
260 if (percentage > 0)
261 percN = 100 / percentage;
262 else
263 alwaysUnlock = true;
264
265 // start all machines in pack
266 for (TMachinesList::iterator it = machinesList.begin();
267 it != machinesList.end() && g_RunTest; ++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; ++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(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(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(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(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(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media)));
365 if (SUCCEEDED(rc))
366 CHECK_ERROR(machine, DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam()));
367 if (SUCCEEDED(rc))
368 CHECK_ERROR(progress, WaitForCompletion(-1));
369 if (SUCCEEDED(rc))
370 RTPrintf("Machine '%ls' deleted.\n", machineName.raw());
371 }
372 return rc;
373}
374
375
376DECLCALLBACK(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
418int 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_UINT64}
428 };
429 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
430 AssertRC(rc);
431 Assert(pArgs != NULL);
432
433 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
434 {
435 switch (rc)
436 {
437 case 'p':
438 {
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 {
455 if (ValueUnion.u32 > 100)
456 {
457 RTPrintf("maximum --lock value is 100.\n"
458 "That means 100 percent of sessions should be closed without unlock.\n");
459 return VERR_INVALID_PARAMETER;
460 }
461 pArgs->percentsUnlok = ValueUnion.u32;
462 break;
463 }
464 case 't':
465 {
466 pArgs->executionTime = ValueUnion.u64 * 1000;
467 break;
468 }
469 case 'u':
470 {
471 if (ValueUnion.u32 > 16000)
472 {
473 RTPrintf("maximum --machines value is 16000.\n"
474 "That means can make no more then 16000 machines for the test.\n");
475 return VERR_INVALID_PARAMETER;
476 }
477 if (ValueUnion.u32 < pArgs->machinesPackSize)
478 {
479 RTPrintf("--machines value should be larger then --packsize value.\n");
480 return VERR_INVALID_PARAMETER;
481 }
482 pArgs->numberMachines = ValueUnion.u32;
483 break;
484 }
485 default:
486 {
487 RTPrintf("Invalid arguments.\n");
488 return rc;
489 }
490 }
491 }
492 return rc;
493}
494
495
496/**
497* Examples:
498* - tstVBoxClientWatcherLoad --packsize 500 --lock 10 --time 14400 --machines 4000
499* It will create 4000 VMs with names "utmvm0"..."utmvm3999"
500* It will start 500 random VMs together, stop them,
501* without closing their session with probability 10%,
502* will repeat this during 4 hours.
503* After test it will 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"
507* It will start random VM - stop them,
508* without closing their session with probability 30%,
509* will repeat this during 30 minutes.
510* After test it will delete all "utmvm..." machines.
511*/
512int main(int argc, char **argv)
513{
514 RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxMultipleVM", &g_hTest);
515 if (rcExit != RTEXITCODE_SUCCESS)
516 return rcExit;
517 SUPR3Init(NULL);
518 com::Initialize();
519 RTTestBanner(g_hTest);
520 RTPrintf("Initializing ...\n");
521 int rc = RTSemEventCreate(&g_PingEevent);
522 AssertRC(rc);
523
524 g_Args.machinesPackSize = 100;
525 g_Args.percentsUnlok = 10;
526 g_Args.executionTime = 3 * 60 * 1000; // 3 minutes of test execution by default
527 g_Args.numberMachines = 200;
528 rc = ParseArguments(argc, argv, &g_Args);
529 if (RT_FAILURE(rc))
530 {
531 return RTTestSkipAndDestroy(g_hTest, "Invalid arguments.\n");
532 }
533 RTPrintf("Arguments packSize = %d, percentUnlok = %d, time = %d.\n",
534 g_Args.machinesPackSize,
535 g_Args.percentsUnlok,
536 g_Args.executionTime);
537
538 RTTHREAD Thread;
539 rc = RTThreadCreate(&Thread, tstThreadRun, (void *)&g_Args,
540 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tstThreadRun");
541 AssertRC(rc);
542
543 uint64_t start = RTTimeMilliTS();
544 while (RTTimeMilliTS() - start < g_Args.executionTime && g_RunTest)
545 {
546 // check that test thread didn't hang and call us periodically
547 // allowed 30 seconds for operation - start or stop VM
548 rc = RTSemEventWait(g_PingEevent, 3 * 60 * 1000);
549 if (RT_FAILURE(rc))
550 {
551 if (rc == VERR_TIMEOUT)
552 {
553 // seems that test thread hungs - alert
554 RTTestFailed(g_hTest, "Test failed - one of operations hunged. VBoxSvc deadlock detected.\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(Thread, RT_INDEFINITE_WAIT, &rc);
568 RTSemEventDestroy(g_PingEevent);
569
570 com::Shutdown();
571 if (RT_FAILURE(rc))
572 {
573 RTTestFailed(g_hTest, "Test failed.\n");
574 return RTTestSummaryAndDestroy(g_hTest);
575 }
576 else
577 {
578 RTTestPassed(g_hTest, "Test finished.\n");
579 }
580 return RTTestSummaryAndDestroy(g_hTest);
581}
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