VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/misc/loadgenerator.cpp@ 78314

Last change on this file since 78314 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: loadgenerator.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * Load Generator.
4 */
5
6/*
7 * Copyright (C) 2007-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/errcore.h>
32#include <iprt/thread.h>
33#include <iprt/time.h>
34#include <iprt/initterm.h>
35#include <iprt/string.h>
36#include <iprt/stream.h>
37#include <iprt/param.h>
38#include <iprt/path.h>
39#include <iprt/process.h>
40#include <iprt/mp.h>
41#include <iprt/asm.h>
42#include <iprt/getopt.h>
43#include <VBox/sup.h>
44
45
46/*********************************************************************************************************************************
47* Global Variables *
48*********************************************************************************************************************************/
49/** Whether the threads should quit or not. */
50static bool volatile g_fQuit = false;
51static const char *g_pszProgramName = NULL;
52
53
54/*********************************************************************************************************************************
55* Internal Functions *
56*********************************************************************************************************************************/
57static int Error(const char *pszFormat, ...);
58
59
60static void LoadGenSpin(uint64_t cNanoSeconds)
61{
62 const uint64_t u64StartTS = RTTimeNanoTS();
63 do
64 {
65 for (uint32_t volatile i = 0; i < 10240 && !g_fQuit; i++)
66 i++;
67 } while (RTTimeNanoTS() - u64StartTS < cNanoSeconds && !g_fQuit);
68}
69
70
71static DECLCALLBACK(int) LoadGenSpinThreadFunction(RTTHREAD hThreadSelf, void *pvUser)
72{
73 NOREF(hThreadSelf);
74 LoadGenSpin(*(uint64_t *)pvUser);
75 return VINF_SUCCESS;
76}
77
78
79static int LoadGenIpiInit(void)
80{
81 /*
82 * Try make sure the support library is initialized...
83 */
84 SUPR3Init(NULL);
85
86 /*
87 * Load the module.
88 */
89 char szPath[RTPATH_MAX];
90 int rc = RTPathAppPrivateArchTop(szPath, sizeof(szPath) - sizeof("/loadgenerator.r0"));
91 if (RT_SUCCESS(rc))
92 {
93 strcat(szPath, "/loadgeneratorR0.r0");
94 void *pvImageBase;
95 rc = SUPR3LoadServiceModule(szPath, "loadgeneratorR0", "LoadGenR0ServiceReqHandler", &pvImageBase);
96 if (RT_SUCCESS(rc))
97 {
98 /* done */
99 }
100 else
101 Error("SUPR3LoadServiceModule(%s): %Rrc\n", szPath, rc);
102 }
103 else
104 Error("RTPathAppPrivateArch: %Rrc\n", rc);
105 return rc;
106}
107
108
109static void LoadGenIpi(uint64_t cNanoSeconds)
110{
111 const uint64_t u64StartTS = RTTimeNanoTS();
112 do
113 {
114 int rc = SUPR3CallR0Service("loadgeneratorR0", sizeof("loadgeneratorR0") - 1,
115 0 /* uOperation */, 1 /* cIpis */, NULL /* pReqHdr */);
116 if (RT_FAILURE(rc))
117 {
118 Error("SUPR3CallR0Service: %Rrc\n", rc);
119 break;
120 }
121 } while (RTTimeNanoTS() - u64StartTS < cNanoSeconds && !g_fQuit);
122}
123
124
125static DECLCALLBACK(int) LoadGenIpiThreadFunction(RTTHREAD hThreadSelf, void *pvUser)
126{
127 LoadGenIpi(*(uint64_t *)pvUser);
128 NOREF(hThreadSelf);
129 return VINF_SUCCESS;
130}
131
132
133static int Error(const char *pszFormat, ...)
134{
135 va_list va;
136 RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgramName);
137 va_start(va, pszFormat);
138 RTStrmPrintfV(g_pStdErr, pszFormat, va);
139 va_end(va);
140 return 1;
141}
142
143
144static int SyntaxError(const char *pszFormat, ...)
145{
146 va_list va;
147 RTStrmPrintf(g_pStdErr, "%s: syntax error: ", g_pszProgramName);
148 va_start(va, pszFormat);
149 RTStrmPrintfV(g_pStdErr, pszFormat, va);
150 va_end(va);
151 return 1;
152}
153
154
155int main(int argc, char **argv)
156{
157 static const struct LOADGENTYPE
158 {
159 const char *pszName;
160 int (*pfnInit)(void);
161 PFNRTTHREAD pfnThread;
162 } s_aLoadTypes[] =
163 {
164 { "spin", NULL, LoadGenSpinThreadFunction },
165 { "ipi", LoadGenIpiInit, LoadGenIpiThreadFunction },
166 };
167 unsigned iLoadType = 0;
168 static RTTHREAD s_aThreads[256];
169 int rc;
170 uint32_t cThreads = 1;
171 bool fScaleByCpus = false;
172 RTTHREADTYPE enmThreadType = RTTHREADTYPE_DEFAULT;
173 RTPROCPRIORITY enmProcPriority = RTPROCPRIORITY_DEFAULT;
174 uint64_t cNanoSeconds = UINT64_MAX;
175
176 RTR3InitExe(argc, &argv, 0);
177
178 /*
179 * Set program name.
180 */
181 g_pszProgramName = RTPathFilename(argv[0]);
182
183 /*
184 * Parse arguments.
185 */
186 static const RTGETOPTDEF s_aOptions[] =
187 {
188 { "--number-of-threads", 'n', RTGETOPT_REQ_UINT32 },
189 { "--timeout", 't', RTGETOPT_REQ_STRING },
190 { "--thread-type", 'p', RTGETOPT_REQ_STRING },
191 { "--scale-by-cpus", 'c', RTGETOPT_REQ_NOTHING },
192 { "--load", 'l', RTGETOPT_REQ_STRING },
193 };
194 int ch;
195 RTGETOPTUNION ValueUnion;
196 RTGETOPTSTATE GetState;
197 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
198 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
199 {
200 switch (ch)
201 {
202 case 'n':
203 cThreads = ValueUnion.u64;
204 if (cThreads == 0 || cThreads > RT_ELEMENTS(s_aThreads))
205 return SyntaxError("Requested number of threads, %RU32, is out of range (1..%d).\n",
206 cThreads, RT_ELEMENTS(s_aThreads) - 1);
207 break;
208
209 case 't':
210 {
211 char *psz;
212 rc = RTStrToUInt64Ex(ValueUnion.psz, &psz, 0, &cNanoSeconds);
213 if (RT_FAILURE(rc))
214 return SyntaxError("Failed reading the alleged number '%s' (option '%s', rc=%Rrc).\n",
215 ValueUnion.psz, rc);
216 while (*psz == ' ' || *psz == '\t')
217 psz++;
218 if (*psz)
219 {
220 uint64_t u64Factor = 1;
221 if (!strcmp(psz, "ns"))
222 u64Factor = 1;
223 else if (!strcmp(psz, "ms"))
224 u64Factor = 1000;
225 else if (!strcmp(psz, "s"))
226 u64Factor = 1000000000;
227 else if (!strcmp(psz, "m"))
228 u64Factor = UINT64_C(60000000000);
229 else if (!strcmp(psz, "h"))
230 u64Factor = UINT64_C(3600000000000);
231 else
232 return SyntaxError("Unknown time suffix '%s'\n", psz);
233 uint64_t u64 = cNanoSeconds * u64Factor;
234 if (u64 < cNanoSeconds || (u64 < u64Factor && u64))
235 return SyntaxError("Time representation overflowed! (%RU64 * %RU64)\n",
236 psz, cNanoSeconds, u64Factor);
237 cNanoSeconds = u64;
238 }
239 break;
240 }
241
242 case 'p':
243 {
244 enmProcPriority = RTPROCPRIORITY_NORMAL;
245
246 uint32_t u32 = RTTHREADTYPE_INVALID;
247 char *psz;
248 rc = RTStrToUInt32Ex(ValueUnion.psz, &psz, 0, &u32);
249 if (RT_FAILURE(rc) || *psz)
250 {
251 if (!strcmp(ValueUnion.psz, "default"))
252 {
253 enmProcPriority = RTPROCPRIORITY_DEFAULT;
254 enmThreadType = RTTHREADTYPE_DEFAULT;
255 }
256 else if (!strcmp(ValueUnion.psz, "idle"))
257 {
258 enmProcPriority = RTPROCPRIORITY_LOW;
259 enmThreadType = RTTHREADTYPE_INFREQUENT_POLLER;
260 }
261 else if (!strcmp(ValueUnion.psz, "high"))
262 {
263 enmProcPriority = RTPROCPRIORITY_HIGH;
264 enmThreadType = RTTHREADTYPE_IO;
265 }
266 else
267 return SyntaxError("can't grok thread type '%s'\n",
268 ValueUnion.psz);
269 }
270 else
271 {
272 enmThreadType = (RTTHREADTYPE)u32;
273 if (enmThreadType <= RTTHREADTYPE_INVALID || enmThreadType >= RTTHREADTYPE_END)
274 return SyntaxError("thread type '%d' is out of range (%d..%d)\n",
275 ValueUnion.psz, RTTHREADTYPE_INVALID + 1, RTTHREADTYPE_END - 1);
276 }
277 break;
278 }
279
280 case 'c':
281 fScaleByCpus = true;
282 break;
283
284 case 'l':
285 {
286 for (unsigned i = 0; i < RT_ELEMENTS(s_aLoadTypes); i++)
287 if (!strcmp(s_aLoadTypes[i].pszName, ValueUnion.psz))
288 {
289 ValueUnion.psz = NULL;
290 iLoadType = i;
291 break;
292 }
293 if (ValueUnion.psz)
294 return SyntaxError("Unknown load type '%s'.\n", ValueUnion.psz);
295 break;
296 }
297
298 case 'h':
299 RTStrmPrintf(g_pStdOut,
300 "Usage: %s [-p|--thread-type <type>] [-t|--timeout <sec|xxx[h|m|s|ms|ns]>] \\\n"
301 " %*s [-n|--number-of-threads <threads>] [-l|--load <loadtype>]\n"
302 "\n"
303 "Load types: spin, ipi.\n"
304 ,
305 g_pszProgramName, strlen(g_pszProgramName), "");
306 return 1;
307
308 case 'V':
309 RTPrintf("$Revision: 76553 $\n");
310 return 0;
311
312 case VINF_GETOPT_NOT_OPTION:
313 return SyntaxError("Unknown argument #%d: '%s'\n", GetState.iNext-1, ValueUnion.psz);
314
315 default:
316 return RTGetOptPrintError(ch, &ValueUnion);
317 }
318 }
319
320 /*
321 * Scale thread count by host cpu count.
322 */
323 if (fScaleByCpus)
324 {
325 const unsigned cCpus = RTMpGetOnlineCount();
326 if (cCpus * cThreads > RT_ELEMENTS(s_aThreads))
327 return SyntaxError("Requested number of threads, %RU32, is out of range (1..%d) when scaled by %d.\n",
328 cThreads, RT_ELEMENTS(s_aThreads) - 1, cCpus);
329 cThreads *= cCpus;
330 }
331
332 /*
333 * Modify process and thread priority? (ignore failure)
334 */
335 if (enmProcPriority != RTPROCPRIORITY_DEFAULT)
336 RTProcSetPriority(enmProcPriority);
337 if (enmThreadType != RTTHREADTYPE_DEFAULT)
338 RTThreadSetType(RTThreadSelf(), enmThreadType);
339
340 /*
341 * Load type specific init.
342 */
343 if (s_aLoadTypes[iLoadType].pfnInit)
344 {
345 rc = s_aLoadTypes[iLoadType].pfnInit();
346 if (RT_FAILURE(rc))
347 return 1;
348 }
349
350
351 /*
352 * Start threads.
353 */
354 for (unsigned i = 1; i < cThreads; i++)
355 {
356 s_aThreads[i] = NIL_RTTHREAD;
357 rc = RTThreadCreate(&s_aThreads[i], s_aLoadTypes[iLoadType].pfnThread,
358 &cNanoSeconds, 128*1024, enmThreadType, RTTHREADFLAGS_WAITABLE, "spinner");
359 if (RT_FAILURE(rc))
360 {
361 ASMAtomicXchgBool(&g_fQuit, true);
362 RTStrmPrintf(g_pStdErr, "%s: failed to create thread #%d, rc=%Rrc\n", g_pszProgramName, i, rc);
363 while (i-- > 1)
364 RTThreadWait(s_aThreads[i], 1500, NULL);
365 return 1;
366 }
367 }
368
369 /* our selves */
370 s_aLoadTypes[iLoadType].pfnThread(RTThreadSelf(), &cNanoSeconds);
371
372 /*
373 * Wait for threads.
374 */
375 ASMAtomicXchgBool(&g_fQuit, true);
376 for (unsigned i = 1; i < cThreads; i++)
377 RTThreadWait(s_aThreads[i], 1500, NULL);
378
379 return 0;
380}
381
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