summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/perf/perfbot_run.py204
1 files changed, 204 insertions, 0 deletions
diff --git a/scripts/perf/perfbot_run.py b/scripts/perf/perfbot_run.py
new file mode 100644
index 0000000000..006d5f8934
--- /dev/null
+++ b/scripts/perf/perfbot_run.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3
+"""\
+@file perfbot_run.py
+@brief Run a number of non interactive Viewers (PerfBots) with
+ a variety of options and settings. Pass --help for details.
+
+$LicenseInfo:firstyear=2007&license=viewerlgpl$
+Second Life Viewer Source Code
+Copyright (C) 2021, Linden Research, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation;
+version 2.1 of the License only.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+$/LicenseInfo$
+"""
+
+import argparse
+import subprocess
+import os
+import math
+import time
+
+# Required parameters that are always passed in
+# Specify noninteractive mode (SL-15999 for details)
+PARAM_NON_INTERACTIVE = "--noninteractive"
+# Run multiple Viewers at once
+PARAM_MULTI = "--multiple"
+# Specify username (first and last) and password
+PARAM_LOGIN = "--login"
+# SLURL to teleport to after login
+PARAM_SLURL = "--slurl"
+
+
+def gen_niv_script(args):
+ print(f"Reading creds from {(args.creds)} folder")
+ print(f"Using the non interactive Viewer from {(args.viewer)}")
+ print(f"Sleeping for {args.sleep}ms between Viewer launches")
+
+ # Read the lines from the creds file. Typically this will be
+ # stored in the build-secrets-git private repository but you
+ # can point to any location with the --creds parameter
+ creds_lines = []
+ with open(args.creds) as file:
+ creds_lines = file.readlines()
+ creds_lines = [line.rstrip() for line in creds_lines]
+ creds_lines = [line for line in creds_lines if not line.startswith("#") and len(line)]
+ # We cannot log in more users than we have credentials for
+ if args.num==0:
+ args.num = len(creds_lines)
+ if args.num > len(creds_lines):
+ print(
+ f"The number of agents specified ({(args.num)}) exceeds "
+ f"the number of valid entries ({(len(creds_lines))}) in "
+ f"the creds file "
+ )
+ return
+
+ print(f"Launching {(args.num)} instances of the Viewer")
+
+ # The Viewer (in dev environments at least) needs a well specified
+ # working directory to function properly. We try to guess what it
+ # might be based on the full path to the Viewer executable but
+ # you can also specify it explicitly with the --cwd parameter
+ # (required for dev builds)
+ args.viewer = os.path.abspath(args.viewer)
+ working_dir = args.cwd
+ if len(args.cwd) == 0:
+ working_dir = os.path.dirname(os.path.abspath(args.viewer))
+ print(f"Working directory is {working_dir} {args.cwd=}")
+ environ = os.environ
+ environ["cwd"] = working_dir
+
+ if args.dryrun:
+ print("Running in dry-run mode - no Viewers will be started")
+ print("")
+
+ for inst in range(args.num):
+
+ # Format of each cred line is username_first username_last password
+ # A space is used to separate each and a # at the start of a line
+ # removes it from the pool (useful if someone else is using a subset
+ # of the available ones)
+ creds = creds_lines[inst].split(" ")
+ username_first = creds[0]
+ username_last = creds[1]
+ password = creds[2]
+
+ # The default layout is an evenly spaced circle in the
+ # center of the region. We may extend this to allow other
+ # patterns like a square/rectangle or a spiral. (Hint: it
+ # likely won't be needed :))
+ center_x = 128
+ center_y = 128
+ if args.layout == "circle":
+ radius = 6
+ angle = (2 * math.pi / args.num) * inst
+ region_x = int(math.sin(angle) * radius + center_x)
+ region_y = int(math.cos(angle) * radius + center_y)
+ region_z = 0
+ elif args.layout == "square":
+ region_x = center_x
+ region_y = center_y
+ elif args.layout == "spiral":
+ region_x = center_x
+ region_y = center_y
+ slurl = f"secondlife://{args.region}/{region_x}/{region_y}/{region_z}"
+
+ # Build the script line
+ script_cmd = [args.viewer]
+ script_cmd.append(PARAM_NON_INTERACTIVE)
+ script_cmd.append(PARAM_MULTI)
+ script_cmd.append(PARAM_LOGIN)
+ script_cmd.append(username_first)
+ script_cmd.append(username_last)
+ script_cmd.append(password)
+ script_cmd.append(PARAM_SLURL)
+ script_cmd.append(slurl)
+
+ # Display the script we will execute.
+ cmd = ""
+ for p in script_cmd:
+ cmd = cmd + " " + p
+ print(cmd)
+
+ # If --dry-run is specified, we do everything (including, most
+ # usefully, display the script lines) but do not start the Viewer
+ if args.dryrun == False:
+ print("opening viewer session with",script_cmd)
+ viewer_session = subprocess.Popen(script_cmd,env=environ)
+
+ # Sleeping a bit between launches seems to help avoid a CPU
+ # surge when N Viewers are started simulatanously. The default
+ # value can be changed with the --sleep parameter
+ time.sleep(args.sleep / 1000)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(allow_abbrev=False)
+ parser.add_argument(
+ "--num",
+ type=int,
+ default=0,
+ dest="num",
+ help="How many avatars to add to the script",
+ )
+ parser.add_argument(
+ "--creds",
+ default="../../../build-secrets-git/perf/perfbot_creds.txt",
+ dest="creds",
+ help="Location of the text file containing user credentials",
+ )
+ parser.add_argument(
+ "--viewer",
+ default="C:/Program Files/SecondLife/SecondLifeViewer.exe",
+ dest="viewer",
+ help="Location of the non interactive Viewer build",
+ )
+ parser.add_argument(
+ "--cwd",
+ default="",
+ dest="cwd",
+ help="Location of the current working directory to use",
+ )
+ parser.add_argument(
+ "--region",
+ default="Lag Me 5",
+ dest="region",
+ help="The SLURL for the Second Life region to visit",
+ )
+ parser.add_argument(
+ "--layout",
+ default="circle",
+ dest="layout",
+ choices={"circle", "square", "spiral"},
+ help="The geometric layout of the avatar destination locations",
+ )
+ parser.add_argument(
+ "--sleep",
+ type=int,
+ default=1000,
+ dest="sleep",
+ help="Time to sleep between launches in milliseconds",
+ )
+ parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ dest="dryrun",
+ help="Dryrun mode - display parameters and script lines but do not start any Viewers",
+ )
+ args = parser.parse_args()
+
+ gen_niv_script(args)