├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING ├── LICENSE ├── PATENTS ├── README ├── benchpress ├── .gitignore ├── README.md ├── benchpress │ ├── __init__.py │ ├── cli.py │ ├── lib │ │ ├── __init__.py │ │ ├── factory.py │ │ ├── hook.py │ │ ├── hook_factory.py │ │ ├── parser.py │ │ ├── reporter.py │ │ └── reporter_factory.py │ ├── plugins │ │ ├── __init__.py │ │ └── hooks │ │ │ ├── __init__.py │ │ │ ├── cpu_limit.py │ │ │ ├── file.py │ │ │ └── shell.py │ └── suites │ │ ├── __init__.py │ │ ├── generic.py │ │ ├── ltp.py │ │ ├── packetdrill.py │ │ ├── suite.py │ │ └── xfstests.py ├── example ├── poetry.lock ├── pyproject.toml ├── suites.yml └── tests │ ├── __init__.py │ ├── test_factory.py │ ├── test_file_hook.py │ ├── test_generic.py │ ├── test_ltp.py │ ├── test_packetdrill.py │ ├── test_shell_hook.py │ ├── test_suite.py │ └── test_xfstests.py ├── bsdacctd ├── Makefile ├── bsdacctd.c └── packman.yml ├── ncrx └── README.md ├── netconsd └── README.md └── netesto ├── Netesto.pdf ├── README ├── local ├── collectExp.sh ├── exp │ ├── bbr3.tgz │ ├── exp.py │ ├── exp.script.all.example │ ├── exp.script.basic │ ├── exp.script.example │ ├── plot-header.ps │ ├── plotFlowsRetransFairness.exp │ ├── plotFlowsRttCwnd.exp │ └── psPlot.py ├── fields.txt ├── inlib ├── inlib.caTest ├── inlib.rateTest ├── inlib.vsTest ├── makeResultsPage.py ├── netesto.py ├── plot-header.ps ├── plotMonitor.py ├── plotNetperfRates.py ├── processExp.py ├── processExp.use ├── psPlot.py ├── script.23 ├── script.23router ├── script.basic ├── script.ca ├── script.complexSample ├── script.rateTest ├── script.sizes ├── script.sizesrouter ├── script.test └── script.vsTest └── remote ├── clients.txt ├── doClient.sh ├── doServer.sh ├── doSs.sh ├── netesto.py ├── netperf.fields ├── netstat.py ├── setParam.sh └── setSysctl.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.d 3 | *.o 4 | *.s 5 | !.gitignore 6 | 7 | # keep travis 8 | !.travis.yml 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Pull Requests 2 | ============= 3 | 4 | We actively welcome your pull requests. 5 | 1. Fork the repo and create your branch from `master`. 6 | 2. If you've added code that should be tested, add tests 7 | 3. If you've changed APIs, update the documentation. 8 | 4. Ensure the test suite passes. 9 | 5. Make sure your code lints. 10 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 11 | 12 | Contributor License Agreement ("CLA") 13 | ===================================== 14 | 15 | In order to accept your pull request, we need you to submit a CLA. You only need 16 | to do this once to work on any of Facebook's open source projects. 17 | 18 | Complete your CLA here: https://code.facebook.com/cla 19 | 20 | Issues 21 | ====== 22 | 23 | We use GitHub issues to track public bugs. Please ensure your description is 24 | clear and has sufficient instructions to be able to reproduce the issue. 25 | 26 | Facebook has a bounty program (https://www.facebook.com/whitehat) for the safe 27 | disclosure of security bugs. In those cases, please go through the process 28 | outlined on that page and do not file a public issue. 29 | 30 | Coding Style 31 | ============ 32 | 33 | Both netconsd and libncrx use the Linux kernel coding style, which is described 34 | in Documentation/CodingStyle in the kernel source. 35 | 36 | License 37 | ======= 38 | 39 | By contributing to fbkutils, you agree that your contributions will be licensed 40 | under its BSD license. 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For fbkutils software 4 | 5 | Copyright (c) 2016-present, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights Version 2 2 | 3 | "Software" means the fbkutils software distributed by Facebook, Inc. 4 | 5 | Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software 6 | ("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable 7 | (subject to the termination provision below) license under any Necessary 8 | Claims, to make, have made, use, sell, offer to sell, import, and otherwise 9 | transfer the Software. For avoidance of doubt, no license is granted under 10 | Facebook’s rights in any patent claims that are infringed by (i) modifications 11 | to the Software made by you or any third party or (ii) the Software in 12 | combination with any software or other technology. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | if you (or any of your subsidiaries, corporate affiliates or agents) initiate 16 | directly or indirectly, or take a direct financial interest in, any Patent 17 | Assertion: (i) against Facebook or any of its subsidiaries or corporate 18 | affiliates, (ii) against any party if such Patent Assertion arises in whole or 19 | in part from any software, technology, product or service of Facebook or any of 20 | its subsidiaries or corporate affiliates, or (iii) against any party relating 21 | to the Software. Notwithstanding the foregoing, if Facebook or any of its 22 | subsidiaries or corporate affiliates files a lawsuit alleging patent 23 | infringement against you in the first instance, and you respond by filing a 24 | patent infringement counterclaim in that lawsuit against that party that is 25 | unrelated to the Software, the license granted hereunder will not terminate 26 | under section (i) of this paragraph due to such counterclaim. 27 | 28 | A "Necessary Claim" is a claim of a patent owned by Facebook that is 29 | necessarily infringed by the Software standing alone. 30 | 31 | A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, 32 | or contributory infringement or inducement to infringe any patent, including a 33 | cross-claim or counterclaim. 34 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | fbkutils: Linux kernel utilities from Facebook 2 | 3 | NOTE: this repo is no longer updated 4 | 5 | ================================================================================ 6 | 7 | ncrx: A library for processing extended netconsole messages from the 8 | Linux kernel. See Documentation/networking/netconsole.txt in the 9 | kernel source for details about extended netconsole. 10 | Moved to https://github.com/facebook/netconsd 11 | 12 | netconsd: A daemon which uses libncrx to receive and process netconsole 13 | messages from a large number of remote hosts. We use this daemon 14 | for collecting kernel logs from all the servers at Facebook. 15 | Moved to https://github.com/facebook/netconsd 16 | 17 | netesto: NEtwork TESting TOls, a set of tools for network testing. It 18 | allows one machine, the controller, to run network load tests 19 | currently consisting of many netperf transfers. The controller 20 | is in charge of controlling the clients (which run netperf) and 21 | servers (which run netserver) and collecting the results. I've 22 | used this framework extensively in the past year to evaluate 23 | TCP-NV and compare various TCP variants such as cubic, nv, bbr, 24 | etc. 25 | 26 | ================================================================================ 27 | 28 | Contributing 29 | ============ 30 | 31 | Contributions to fbkutils are welcome and encouraged! 32 | 33 | Please read the guidelines in CONTRIBUTING, and make sure you've signed the CLA 34 | before sending the pull request. 35 | 36 | Whitehat 37 | ======== 38 | 39 | Facebook has a bounty program (https://www.facebook.com/whitehat) for the the 40 | safe disclosure of security bugs. If you find a vulnerability, please go through 41 | the process outlined on that page and do not file a public issue. 42 | -------------------------------------------------------------------------------- /benchpress/.gitignore: -------------------------------------------------------------------------------- 1 | # benchmark output 2 | results/ 3 | 4 | # benchmarks binaries 5 | benchmarks/ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /benchpress/README.md: -------------------------------------------------------------------------------- 1 | Benchpress 2 | ========== 3 | 4 | Benchpress (BENCHmark Pluggable haRnESS) is a framework for running kernel 5 | correctness test suites. The name is historical, when the project was originally 6 | intended to run benchmarks, but it has since pivoted to running correctness 7 | tests only. 8 | Benchpress takes a configuration-based approach to running tests, specifying 9 | configuration in yaml files. 10 | 11 | Installation 12 | ------------ 13 | 14 | `benchpress` requires `python3` 15 | `benchpress` supports `python` 3.3 and higher, and is currently tested on 16 | `python 3.3,3.4,3.5` and `3.6`. 17 | 18 | `benchpress` can be installed with `pip install fb-benchpress` 19 | 20 | Running benchpress 21 | ------------------ 22 | 23 | The `benchpress` cli is simple to use, simply give it the paths to the 24 | benchmarks and jobs definition files, along with an optional list of 25 | benchmark jobs to run (if this is omitted all defined jobs are run). 26 | 27 | Examples: 28 | List available tests: 29 | `benchpress -b benchmarks.yml -j jobs/jobs.yml list` 30 | Run all tests defined in `jobs/jobs.yml`: 31 | `benchpress -b benchmarks.yml -j jobs/jobs.yml run` 32 | Run just the "fio aio" test: 33 | `benchpress -b benchmarks.yml -j jobs/jobs.yml run "fio aio"` 34 | 35 | How benchpress works 36 | -------------------- 37 | 38 | `benchpress` is configured with 'jobs' and 'suites'. A suite is a 39 | reference to a binary (that usually can be run with different arguments). 40 | A job is a specific run of that suite with a specific configuration (eg: suite = xfstests, job = xfstests-btrfs) 41 | 42 | 43 | Parsers 44 | ------- 45 | 46 | Once you've defined your benchmarks and jobs, each benchmark needs a parser. A 47 | parser is a piece of Python code that can parse the output of a benchmark 48 | binary. A Parser is a subclass of `benchpress.lib.parser.Parser`, it must 49 | implement the `parse` method, which returns a dictionary of metrics given the 50 | stdout and stderr of the binary. 51 | 52 | Adding a plugin entails writing the `Parser` implementation and adding it to 53 | `register_parsers` in `benchpress/plugins/parsers/__init__.py` 54 | 55 | Reporting 56 | --------- 57 | 58 | By default, `benchpress` simply reports job results to stdout, this can be 59 | customized by creating a subclass of `benchpress.lib.reporter.Reporter` and 60 | registering it with the `ReporterFactory` 61 | -------------------------------------------------------------------------------- /benchpress/benchpress/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookarchive/fbkutils/676b68089ffff7c3c8e6872bcbcf5d6f404eb603/benchpress/benchpress/__init__.py -------------------------------------------------------------------------------- /benchpress/benchpress/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | import os 11 | import sys 12 | 13 | import click 14 | import yaml 15 | from benchpress.lib.reporter import JSONReporter, StdoutReporter 16 | from benchpress.lib.reporter_factory import ReporterFactory 17 | from benchpress.suites.suite import DiscoveredTestCase, Suite 18 | 19 | 20 | # register reporter plugins before setting up the parser 21 | ReporterFactory.register("stdout", StdoutReporter) 22 | ReporterFactory.register("default", StdoutReporter) 23 | ReporterFactory.register("json", JSONReporter) 24 | 25 | 26 | @click.group() 27 | @click.option("-v", "--verbose", count=True, default=0) 28 | @click.option( 29 | "-s", 30 | "--suites", 31 | type=click.File("r"), 32 | default=lambda: os.environ.get("BENCHPRESS_SUITES", "suites.yml"), 33 | ) 34 | @click.pass_context 35 | def benchpress(ctx, verbose, suites): 36 | ctx.ensure_object(dict) 37 | 38 | # warn is 30, should default to 30 when verbose=0 39 | # each level below warning is 10 less than the previous 40 | log_level = verbose * (-10) + 30 41 | logging.basicConfig(format="%(levelname)s:%(name)s: %(message)s", level=log_level) 42 | logger = logging.getLogger(__name__) 43 | 44 | logger.info('Loading suites from "{}"'.format(suites)) 45 | suite_configs = yaml.load(suites) 46 | 47 | suites = [Suite.instantiate(s) for s in suite_configs] 48 | suites = {s.name: s for s in suites} 49 | ctx.obj["suites"] = suites 50 | 51 | logger.info("Loaded {} test suites".format(len(suites))) 52 | 53 | 54 | @benchpress.command() 55 | @click.option("--suite", "-s") 56 | @click.option("--case", "-c", "cases", multiple=True) 57 | @click.option("--json", "-j", "output_json", is_flag=True) 58 | @click.pass_context 59 | def run(ctx, suite, cases, output_json): 60 | logger = logging.getLogger("benchpress.run") 61 | 62 | reporter = ReporterFactory.create("default") 63 | if suite not in ctx.obj["suites"]: 64 | logger.error('No suite "{}" found'.format(suite)) 65 | sys.exit(1) 66 | suite = ctx.obj["suites"][suite] 67 | 68 | print(f'Running "{suite.name}"') 69 | if not cases: 70 | cases = None 71 | else: 72 | cases = [DiscoveredTestCase(name=c, description="") for c in cases] 73 | results = suite.run(cases) 74 | if output_json: 75 | reporter = ReporterFactory.create("json") 76 | reporter.report(suite, results) 77 | reporter.close() 78 | 79 | 80 | @benchpress.command("list") 81 | @click.argument("suite", required=False) 82 | @click.pass_context 83 | def list_suites(ctx, suite): 84 | suites = ctx.obj["suites"] 85 | if suite: 86 | # list test cases in a suite 87 | suite = suites[suite] 88 | cases = suite.discover_cases() 89 | print(f'Test cases in "{suite.name}":') 90 | for case in cases: 91 | print(f" {case.name}: {case.description}") 92 | return 93 | for suite in suites.values(): 94 | click.echo("{}: {}".format(suite.name, suite.description)) 95 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookarchive/fbkutils/676b68089ffff7c3c8e6872bcbcf5d6f404eb603/benchpress/benchpress/lib/__init__.py -------------------------------------------------------------------------------- /benchpress/benchpress/lib/factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | 10 | class BaseFactory(object): 11 | """Factory to construct instances of classes based on name. 12 | 13 | Attributes: 14 | base_class (class): base class that registered classes must subclass 15 | """ 16 | 17 | def __init__(self, base_class): 18 | """Create a BaseFactory with base_class as the supertype.""" 19 | self.base_class = base_class 20 | self.classes = {} 21 | 22 | @property 23 | def registered_names(self): 24 | """list of str: class names registered with the factory.""" 25 | return list(self.classes.keys()) 26 | 27 | def create(self, name): 28 | """Find the subclass with the correct name and instantiates it. 29 | 30 | Args: 31 | name (str): name of the item 32 | """ 33 | if name not in self.classes: 34 | raise KeyError( 35 | 'No type "{}". ' "Did you forget to register() it?".format(name) 36 | ) 37 | return self.classes[name]() 38 | 39 | def register(self, name, subclass): 40 | """Registers a class with the factory. 41 | 42 | Args: 43 | name (str): name of the class 44 | subclass (class): concrete subclass of base_class 45 | """ 46 | assert issubclass(subclass, self.base_class) 47 | self.classes[name] = subclass 48 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/hook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | from abc import ABCMeta, abstractmethod 10 | 11 | 12 | class Hook(object, metaclass=ABCMeta): 13 | """Hook allows jobs to run some Python code before/after a job runs.""" 14 | 15 | @abstractmethod 16 | def before(self, opts, job): 17 | """Do something to setup before this job. 18 | 19 | Args: 20 | opts (dict): user-defined options for this hook 21 | """ 22 | 23 | @abstractmethod 24 | def after(self, opts, job): 25 | """Do something to teardown after this job. 26 | 27 | Args: 28 | opts (dict): user-defined options for this hook 29 | """ 30 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/hook_factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | from benchpress.lib.factory import BaseFactory 10 | from benchpress.lib.hook import Hook 11 | from benchpress.plugins.hooks import register_hooks 12 | 13 | 14 | HookFactory = BaseFactory(Hook) 15 | 16 | # register third-party hooks with the factory 17 | register_hooks(HookFactory) 18 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | import enum 9 | from abc import ABCMeta, abstractmethod 10 | from dataclasses import dataclass 11 | from typing import Dict, List, Optional 12 | 13 | 14 | class TestStatus(enum.IntEnum): 15 | __test__ = False # keep pytest from picking these up as tests 16 | 17 | PASSED = 1 18 | FAILED = 2 19 | SKIPPED = 3 20 | FATAL = 4 21 | TIMEOUT = 5 22 | OMITTED = 7 23 | 24 | 25 | @dataclass 26 | class TestCaseResult(object): 27 | __test__ = False # keep pytest from picking these up as tests 28 | 29 | name: str 30 | 31 | status: TestStatus 32 | 33 | description: Optional[str] = None 34 | """more details about what this test case is meant to test""" 35 | 36 | details: Optional[str] = "" 37 | """detailed output from this test case for a debugging aid""" 38 | 39 | runtime: Optional[float] = None 40 | """runtime of this test case in seconds""" 41 | 42 | metrics: Optional[Dict[str, float]] = None 43 | """metrics can be used for performance testing, they are not natively used by 44 | benchpress but can be used by a wrapper using benchpress as a test runner.""" 45 | 46 | 47 | class Parser(object, metaclass=ABCMeta): 48 | """Parser is the link between test output and the rest of the system. 49 | A Parser is given the test's stdout and stderr and returns the exported 50 | test cases. 51 | """ 52 | 53 | @abstractmethod 54 | def parse( 55 | self, stdout: List[str], stderr: List[str], returncode: int 56 | ) -> List[TestCaseResult]: 57 | """Take stdout/stderr and convert it to a list of test cases.""" 58 | pass 59 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/reporter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | import dataclasses 9 | import json 10 | from abc import ABCMeta, abstractmethod 11 | from typing import Iterable, List 12 | 13 | from benchpress.lib.parser import TestCaseResult, TestStatus 14 | from benchpress.suites import Suite 15 | 16 | 17 | class Reporter(object, metaclass=ABCMeta): 18 | """A Reporter is used to record suite results in your infrastructure.""" 19 | 20 | @abstractmethod 21 | def report(self, suite: Suite, results: List[TestCaseResult]): 22 | """Save suite metrics somewhere in existing monitoring infrastructure.""" 23 | pass 24 | 25 | @abstractmethod 26 | def close(self): 27 | """Do whatever necessary cleanup is required after all suites are finished.""" 28 | pass 29 | 30 | 31 | class StdoutReporter(Reporter): 32 | """Default reporter implementation, logs a human-readable line to stdout.""" 33 | 34 | def report(self, suite: Suite, results: List[TestCaseResult]): 35 | for case in results: 36 | color = "\u001b[32m" if case.status == TestStatus.PASSED else "\u001b[31m" 37 | print(f"{case.name}: {color}{case.status.name}\033[0m") 38 | if case.details: 39 | lines = case.details.split("\n") 40 | for line in lines: 41 | print(f" {line}") 42 | if case.metrics: 43 | print(" metrics:") 44 | for key, value in case.metrics: 45 | print(f" {key}={value}") 46 | 47 | def close(self): 48 | pass 49 | 50 | 51 | class JSONReporter(Reporter): 52 | def report(self, suite: Suite, results: Iterable[TestCaseResult]): 53 | """Log JSON report to stdout. 54 | Attempt to detect whether a real person is running the program then 55 | pretty print, otherwise print it as JSON. 56 | """ 57 | for case in results: 58 | print(json.dumps(dataclasses.asdict(case)), flush=True) 59 | 60 | def close(self): 61 | pass 62 | -------------------------------------------------------------------------------- /benchpress/benchpress/lib/reporter_factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | from benchpress.lib.factory import BaseFactory 10 | from benchpress.lib.reporter import Reporter 11 | 12 | 13 | ReporterFactory = BaseFactory(Reporter) 14 | -------------------------------------------------------------------------------- /benchpress/benchpress/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookarchive/fbkutils/676b68089ffff7c3c8e6872bcbcf5d6f404eb603/benchpress/benchpress/plugins/__init__.py -------------------------------------------------------------------------------- /benchpress/benchpress/plugins/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | from benchpress.plugins.hooks.cpu_limit import CpuLimit 10 | from benchpress.plugins.hooks.file import FileHook 11 | from benchpress.plugins.hooks.shell import ShellHook 12 | 13 | 14 | def register_hooks(factory): 15 | factory.register("cpu-limit", CpuLimit) 16 | factory.register("file", FileHook) 17 | factory.register("shell", ShellHook) 18 | -------------------------------------------------------------------------------- /benchpress/benchpress/plugins/hooks/cpu_limit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | 11 | from benchpress.lib.hook import Hook 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | class CpuLimit(Hook): 18 | """CpuLimit hook allows you to limit the benchmark to a set of CPUs using 19 | `taskset`. The only option is a hex string bitmask that is the CPU mask 20 | passed to `taskset`, for each bit, if there is a 1 the CPU is enabled for 21 | the benchmark process, otherwise it's disabled. 22 | """ 23 | 24 | def before(self, opts, job): 25 | mask = str(opts) 26 | # try to parse the mask as a hex string as a basic sanity check 27 | try: 28 | int(mask, 16) 29 | except ValueError: 30 | raise ValueError("{} is not a valid CPU mask".format(mask)) 31 | 32 | # modify the job config to run taskset with the given mask instead of 33 | # directly running the benchmark binary 34 | binary = job.config["path"] 35 | job.args = [mask, binary] + job.args 36 | job.binary = "taskset" 37 | 38 | def after(self, opts, job): 39 | pass 40 | -------------------------------------------------------------------------------- /benchpress/benchpress/plugins/hooks/file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import errno 10 | import logging 11 | import os 12 | import shutil 13 | 14 | from benchpress.lib.hook import Hook 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | class FileHook(Hook): 21 | """FileHook provides the ability to create and delete files/directories. 22 | Options are specified as a list of dictionaries - each dictionary must have 23 | a 'type' and a 'path', 'type' is either 'dir' or 'file', and path is where 24 | it will live on the filesystem. Files/directories are created before the 25 | job runs and destroyed after. 26 | """ 27 | 28 | def before(self, opts, job): 29 | for opt in opts: 30 | path = opt["path"] 31 | logger.info('Creating "{}"'.format(path)) 32 | if opt["type"] == "dir": 33 | try: 34 | os.makedirs(path) 35 | except OSError as e: 36 | if e.errno == errno.EEXIST: 37 | logger.warning( 38 | '"{}" already exists, proceeding anyway'.format(path) 39 | ) 40 | else: 41 | # other errors should be fatal 42 | raise 43 | if opt["type"] == "file": 44 | os.mknod(path) 45 | 46 | def after(self, opts, job): 47 | for opt in opts: 48 | path = opt["path"] 49 | logger.info('Deleting "{}"'.format(path)) 50 | if opt["type"] == "dir": 51 | shutil.rmtree(path) 52 | if opt["type"] == "file": 53 | os.unlink(path) 54 | -------------------------------------------------------------------------------- /benchpress/benchpress/plugins/hooks/shell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | import os 11 | import shlex 12 | import subprocess 13 | 14 | from benchpress.lib.hook import Hook 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | class ShellHook(Hook): 21 | """ShellHook provides the ability to run arbitrary shell commands 22 | before/after a job 23 | Options are a dictioanry of 'before' and 'after' lists with a string for 24 | each command to run. 25 | Commands are not run in a shell, so 'cd's are converted to os.chdir 26 | A 'cd' can be executed and will change the working directory of the running 27 | test binary, and is reverted to the previous working directory during the 28 | post hook. 29 | """ 30 | 31 | def __init__(self): 32 | self.original_dir = os.getcwd() 33 | 34 | @staticmethod 35 | def run_commands(cmds): 36 | for cmd in cmds: 37 | # running with shell=True means we should give command as a string 38 | # and not pre-process it 39 | split = shlex.split(cmd) 40 | if split[0] == "cd": 41 | assert len(split) == 2 42 | dst = split[1] 43 | logger.info('Switching to dir "%s"', dst) 44 | os.chdir(dst) 45 | else: 46 | logger.info('Running "%s"', cmd) 47 | subprocess.check_call( 48 | cmd, 49 | shell=True, 50 | stdout=subprocess.DEVNULL, 51 | stderr=subprocess.DEVNULL, 52 | ) 53 | 54 | def before(self, opts, job=None): 55 | self.original_dir = os.getcwd() 56 | if "before" in opts: 57 | self.run_commands(opts["before"]) 58 | 59 | def after(self, opts, job=None): 60 | if "after" in opts: 61 | self.run_commands(opts["after"]) 62 | 63 | # cd back to the original dir in case a command changed it 64 | if os.getcwd() != self.original_dir: 65 | logger.info('Returning to "%s"', self.original_dir) 66 | os.chdir(self.original_dir) 67 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | isort:skip_file 4 | """ 5 | # flake8: noqa: F401 6 | from benchpress.suites.suite import Suite 7 | 8 | from benchpress.suites.generic import GenericSuite 9 | from benchpress.suites.ltp import LtpSuite 10 | from benchpress.suites.packetdrill import PacketdrillSuite 11 | from benchpress.suites.xfstests import XfstestsSuite 12 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/generic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | import re 11 | from typing import Iterable, List, Optional 12 | 13 | from benchpress.lib.parser import TestCaseResult, TestStatus 14 | from benchpress.suites.suite import Suite 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | LINE_REGEX = re.compile(r"^(.*?):?\s+(PASS|FAIL)$") 20 | 21 | 22 | class GenericSuite(Suite): 23 | """GenericSuite is a way to add simple tests that don't support test case 24 | discovery or running a subset of test cases. This is appropriate for simple 25 | tests that just exit(0) to signal passing. 26 | 27 | We will attempt to parse some common form of output such as: 28 | case1: PASS 29 | case2 PASSED 30 | case4 FAIL 31 | case5: FAILED 32 | case6: TIMEOUT 33 | 34 | More complex test suites are encouraged to implement their own subclass of 35 | Suite to gain more useful functionality around test discovery and running 36 | subsets of tests which is very useful when debugging test failures.""" 37 | 38 | NAME = "generic" 39 | 40 | @staticmethod 41 | def get_status_from_name(status: str): 42 | status = status.upper() 43 | try: 44 | return TestStatus[status] 45 | except KeyError: 46 | try: 47 | return TestStatus[status + "ED"] 48 | except KeyError: 49 | logger.warning(f'No such status "{status}(ED)"') 50 | return None 51 | 52 | @staticmethod 53 | def parse_line(line: str) -> Optional[TestCaseResult]: 54 | match = LINE_REGEX.match(line) 55 | if match: 56 | name = match.group(1) 57 | status = GenericSuite.get_status_from_name(match.group(2)) 58 | if status: 59 | return TestCaseResult(name=name, status=status) 60 | return None 61 | 62 | def parse( 63 | self, stdout: List[str], stderr: List[str], returncode: int 64 | ) -> Iterable[TestCaseResult]: 65 | for output in (stdout, stderr): 66 | for line in output: 67 | case = self.parse_line(line) 68 | if case: 69 | yield case 70 | # get a status for pseudo-case "generic" that is based on the exit code 71 | # and contains the subprocess exit code 72 | status = TestStatus.PASSED if returncode == 0 else TestStatus.FAILED 73 | yield TestCaseResult( 74 | name="generic", 75 | status=status, 76 | details=f"exit code: {returncode}\n" 77 | + "stdout:\n" 78 | + "\n".join(stdout) 79 | + "\n\nstderr:\n" 80 | + "\n".join(stderr), 81 | ) 82 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/ltp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | import re 11 | from typing import Iterable, List, Optional 12 | 13 | from benchpress.lib.parser import TestCaseResult, TestStatus 14 | from benchpress.suites.suite import DiscoveredTestCase, Suite 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | test_format_re = re.compile(r"(\w+)+\s+(\d+)\s+(T(?:FAIL|PASS|BROK|WARN|INFO)).*") 19 | 20 | test_case_re = re.compile(r"^(.*)_\d+$") 21 | 22 | 23 | class LtpSuite(Suite): 24 | NAME = "ltp" 25 | 26 | def discover_cases(self) -> List[DiscoveredTestCase]: 27 | # TODO 28 | pass 29 | 30 | def run( 31 | self, cases: Optional[List[DiscoveredTestCase]] = None 32 | ) -> Iterable[TestCaseResult]: 33 | if cases: 34 | names = [] 35 | for c in cases: 36 | name = c.name 37 | match = test_case_re.match(name) 38 | if match: 39 | name = match.group(1) 40 | logger.warning( 41 | "LTP does not support running with a granularity less " 42 | "than a single test case, dropping the extra part " 43 | f"(using '{name}')" 44 | ) 45 | names.append(name) 46 | 47 | self.args += ["-s"] + names 48 | return super().run(cases) 49 | 50 | def parse( 51 | self, stdout: List[str], stderr: List[str], returncode: int 52 | ) -> Iterable[TestCaseResult]: 53 | return self.test_cases(stdout) 54 | 55 | def test_cases(self, stdout: List[str]) -> Iterable[TestCaseResult]: 56 | case_lines: List[str] = [] 57 | for line in stdout: 58 | if line == "<<>>": 59 | case_lines = [] 60 | if line == "<<>>": 61 | output_start = case_lines.index("<<>>") 62 | output_end = case_lines.index("<<>>") 63 | output = case_lines[output_start + 1 : output_end] 64 | 65 | for status_line in output: 66 | match = test_format_re.match(status_line) 67 | if not match: 68 | continue 69 | case_name = match.group(1) + "_" + match.group(2) 70 | status_name = match.group(3) 71 | status = TestStatus.SKIPPED 72 | if status_name in ("TFAIL", "TBROK", "TWARN"): 73 | status = TestStatus.FAILED 74 | elif status_name == "TPASS": 75 | status = TestStatus.PASSED 76 | elif status_name == "TINFO": 77 | continue 78 | else: 79 | logger.warning(f"Encountered unknown status '{status_name}'") 80 | continue 81 | case = TestCaseResult( 82 | name=case_name, status=status, details="\n".join(output) 83 | ) 84 | yield case 85 | case_lines.append(line) 86 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/packetdrill.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | import re 11 | from typing import List 12 | 13 | from benchpress.lib.parser import TestCaseResult, TestStatus 14 | from benchpress.suites.suite import DiscoveredTestCase, Suite 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | LINE_REGEX = re.compile(r"^(.*?):?\s+(PASS|FAIL)$") 20 | 21 | 22 | class PacketdrillSuite(Suite): 23 | NAME = "packetdrill" 24 | 25 | def discover_cases(self) -> List[DiscoveredTestCase]: 26 | # TODO 27 | return [DiscoveredTestCase(name="exec", description="does the test exit(0)")] 28 | 29 | def parse( 30 | self, stdout: List[str], stderr: List[str], returncode: int 31 | ) -> List[TestCaseResult]: 32 | """ 33 | Packetdrill test output is very simple (for now). One row for each 34 | test: 35 | test_name return_value 36 | So the parsing is simple: 37 | "test_name 0" => "test_name PASS" 38 | "test_name non-zero" => "test_name FAIL" 39 | """ 40 | test_cases: List[TestCaseResult] = [] 41 | for line in stdout: 42 | items = line.split() 43 | if len(items) != 2: 44 | continue 45 | 46 | test_name = items[0] 47 | case = TestCaseResult(name=test_name, status=TestStatus.FAILED) 48 | if items[1] == "0": 49 | case.status = TestStatus.PASSED 50 | 51 | test_cases.append(case) 52 | 53 | return test_cases 54 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/suite.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import errno 10 | import logging 11 | import subprocess 12 | import sys 13 | from abc import abstractmethod 14 | from dataclasses import dataclass 15 | from subprocess import CalledProcessError, TimeoutExpired 16 | from typing import Any, Dict, Iterable, List, Optional, Type 17 | 18 | from benchpress.lib.hook_factory import HookFactory 19 | from benchpress.lib.parser import TestCaseResult 20 | 21 | 22 | logger = logging.getLogger(__name__) 23 | 24 | 25 | @dataclass(frozen=True) 26 | class DiscoveredTestCase(object): 27 | name: str 28 | description: Optional[str] 29 | 30 | 31 | class SuiteMeta(type): 32 | 33 | suite_classes: Dict[str, Type["Suite"]] = {} 34 | 35 | @classmethod 36 | def instantiate(metacls, config: Dict[str, Any]) -> "Suite": 37 | runner = config.get("runner", "generic") 38 | if runner not in metacls.suite_classes: 39 | raise RuntimeError(f'No such suite runner "{runner}"') 40 | suite_cls = metacls.suite_classes[runner] 41 | return suite_cls(config) 42 | 43 | def __init__(cls, name, bases, namespace, **kwargs): 44 | super().__init__(name, bases, namespace, **kwargs) 45 | if "NAME" in namespace: 46 | SuiteMeta.suite_classes[namespace["NAME"]] = cls 47 | 48 | 49 | class Suite(metaclass=SuiteMeta): 50 | def __init__(self, config: Dict[str, Any]): 51 | self.config = config 52 | 53 | self.name = config["name"] 54 | self.description = config["description"] 55 | 56 | self.binary = config["path"] 57 | self.args = self.arg_list(config["args"]) 58 | self.check_returncode = config.get("check_returncode", True) 59 | self.timeout = config.get("timeout", None) 60 | self.timeout_is_pass = config.get("timeout_is_pass", False) 61 | # if tee_output is True, the stdout and stderr commands of the child 62 | # process will be copied onto the stdout and stderr of benchpress 63 | # if this option is a string, the output will be written to the file 64 | # named by this value 65 | self.tee_output = config.get("tee_output", False) 66 | 67 | self.hooks = config.get("hooks", []) 68 | self.hooks = [ 69 | (HookFactory.create(h["hook"]), h.get("options", None)) for h in self.hooks 70 | ] 71 | 72 | @staticmethod 73 | def arg_list(args): 74 | """Convert argument definitions to a list suitable for subprocess.""" 75 | if isinstance(args, list): 76 | return args 77 | 78 | lst = [] 79 | for key, val in args.items(): 80 | lst.append("--" + key) 81 | if val is not None: 82 | lst.append(str(val)) 83 | return lst 84 | 85 | def run_pre_hooks(self): 86 | logger.info('Running setup hooks for "{}"'.format(self.name)) 87 | for hook, opts in self.hooks: 88 | logger.info("Running %s %s", hook, opts) 89 | hook.before(opts, self) 90 | 91 | def run_post_hooks(self): 92 | logger.info('Running cleanup hooks for "{}"'.format(self.name)) 93 | # run hooks in reverse this time so it operates like a stack 94 | for hook, opts in reversed(self.hooks): 95 | hook.after(opts, self) 96 | 97 | def run_to_completion(self) -> subprocess.CompletedProcess: 98 | """run_to_completion can be used when streaming output is not requried""" 99 | logger.info('Starting "{}"'.format(self.name)) 100 | cmd = [self.binary] + self.args 101 | try: 102 | proc = subprocess.Popen( 103 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" 104 | ) 105 | stdout, stderr = proc.communicate(None, timeout=self.timeout) 106 | # other parts of the code rely on stdout/err being strings on the 107 | # process itself (this is how subprocess.run works) 108 | proc.stdout = stdout 109 | proc.stderr = stderr 110 | if self.check_returncode: 111 | if proc.returncode != 0: 112 | raise CalledProcessError( 113 | proc.returncode, cmd, output=stdout, stderr=stderr 114 | ) 115 | # optionally copy stdout/err of the child process to our own 116 | if self.tee_output: 117 | # default to stdout if no filename given 118 | tee = sys.stdout 119 | # if a file was specified, write to that file instead 120 | if isinstance(self.tee_output, str): 121 | tee = open(self.tee_output, "w") 122 | # do this so each line is prefixed with stdout 123 | for line in stdout.splitlines(): 124 | tee.write(f"stdout: {line}\n") 125 | for line in stderr.splitlines(): 126 | tee.write(f"stderr: {line}\n") 127 | # close the output if it was a file 128 | if tee != sys.stdout: 129 | tee.close() 130 | 131 | return proc 132 | except OSError as e: 133 | logger.error('"{}" failed ({})'.format(self.name, e)) 134 | if e.errno == errno.ENOENT: 135 | logger.error("Binary not found, did you forget to install it?") 136 | raise # make sure it passes the exception up the chain 137 | except CalledProcessError as e: 138 | logger.error(e.output) 139 | raise # make sure it passes the exception up the chain 140 | except TimeoutExpired: 141 | proc.kill() 142 | raise 143 | 144 | def discover_cases(self) -> List[DiscoveredTestCase]: 145 | return [DiscoveredTestCase(name="exec", description="does the test exit(0)")] 146 | 147 | @abstractmethod 148 | def parse( 149 | self, stdout: List[str], stderr: List[str], returncode: Optional[int] 150 | ) -> Iterable[TestCaseResult]: 151 | pass 152 | 153 | def run( 154 | self, cases: Optional[List[DiscoveredTestCase]] = None 155 | ) -> Iterable[TestCaseResult]: 156 | # default run implementation requires a parse method that takes 157 | # std{out,err} as lists of strings 158 | self.run_pre_hooks() 159 | try: 160 | proc = self.run_to_completion() 161 | stdout = proc.stdout.splitlines() 162 | stderr = proc.stderr.splitlines() 163 | return self.parse(stdout, stderr, proc.returncode) 164 | except TimeoutExpired as e: 165 | stdout, stderr = e.stdout, e.stderr 166 | if not self.timeout_is_pass: 167 | logger.error( 168 | "Job timed out\n" "stdout:\n{}\nstderr:\n{}".format(stdout, stderr) 169 | ) 170 | raise 171 | # if timeout was success, parse the output 172 | # we cannot get output from a timed out process that has children 173 | # because of how Popen.communicate works 174 | stdout = ["timed out as expected"] 175 | stderr = [] 176 | # for generic tests, set the exit code to 0 even though it didn't 177 | # actually pass 178 | return self.parse(stdout, stderr, 0) 179 | finally: 180 | self.run_post_hooks() 181 | -------------------------------------------------------------------------------- /benchpress/benchpress/suites/xfstests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import difflib 10 | import logging 11 | import os 12 | import re 13 | from typing import Iterable, List, Optional 14 | 15 | from benchpress.lib.parser import TestCaseResult, TestStatus 16 | from benchpress.suites.suite import DiscoveredTestCase, Suite 17 | 18 | 19 | logger = logging.getLogger(__name__) 20 | 21 | TESTS_DIR = "xfstests/tests" 22 | RESULTS_DIR = "xfstests/results" 23 | 24 | 25 | class XfstestsSuite(Suite): 26 | NAME = "xfstests" 27 | 28 | def discover_cases(self) -> List[DiscoveredTestCase]: 29 | # TODO 30 | return [DiscoveredTestCase(name="exec", description="does the test exit(0)")] 31 | 32 | @staticmethod 33 | def get_status_from_name(status: str): 34 | status = status.upper() 35 | try: 36 | return TestStatus[status] 37 | except KeyError: 38 | try: 39 | return TestStatus[status + "ED"] 40 | except KeyError: 41 | logger.warning(f'No such status "{status}(ED)"') 42 | return None 43 | 44 | def run( 45 | self, cases: Optional[List[DiscoveredTestCase]] = None 46 | ) -> Iterable[TestCaseResult]: 47 | if cases: 48 | logger.warning( 49 | "benchpress currently doesn't support running groups" 50 | " of tests in xfstests, assuming you passed a list of individual" 51 | " test cases to run" 52 | ) 53 | self.args += [c.name for c in cases] 54 | return super().run(cases) 55 | 56 | def parse( 57 | self, stdout: List[str], stderr: List[str], returncode: int 58 | ) -> Iterable[TestCaseResult]: 59 | excluded = {} 60 | # The exclude list is one test per line optionally followed by a 61 | # comment explaining why the test is excluded. 62 | exclude_list_re = re.compile( 63 | r"\s*(?P[^\s#]+)\s*(?:#\s*(?P.*))?\s*" 64 | ) 65 | try: 66 | with open("exclude_list", "r", errors="backslashreplace") as f: 67 | for line in f: 68 | match = exclude_list_re.match(line) 69 | if match: 70 | reason = match.group("reason") 71 | if reason is None: 72 | reason = "" 73 | excluded[match.group("test_name")] = reason 74 | except OSError: 75 | pass 76 | 77 | test_regex = re.compile( 78 | r"^(?P\w+/\d+)\s+(?:\d+s\s+\.\.\.\s+)?(?P.*)" 79 | ) 80 | for line in stdout: 81 | match = test_regex.match(line) 82 | if match: 83 | test_name = match.group("test_name") 84 | 85 | case = TestCaseResult(name=test_name, status=TestStatus.FATAL) 86 | 87 | status = match.group("status") 88 | duration_match = re.fullmatch(r"(\d+(?:\.\d+)?)s", status) 89 | if duration_match: 90 | case.status = TestStatus.PASSED 91 | case.runtime = float(duration_match.group(1)) 92 | elif status.startswith("[not run]"): 93 | case.status = TestStatus.SKIPPED 94 | case.details = self.not_run_details(test_name) 95 | elif status.startswith("[expunged]"): 96 | case.status = TestStatus.OMITTED 97 | case.details = self.excluded_details(excluded, test_name) 98 | else: 99 | case.status = TestStatus.FAILED 100 | case.details = self.run_details(test_name) 101 | 102 | yield case 103 | 104 | def not_run_details(self, test_name): 105 | try: 106 | notrun = os.path.join(RESULTS_DIR, test_name + ".notrun") 107 | with open(notrun, "r", errors="backslashreplace") as f: 108 | return "Not run: " + f.read().strip() 109 | except OSError: 110 | return "Not run" 111 | 112 | @staticmethod 113 | def excluded_details(excluded, test_name): 114 | try: 115 | return "Excluded: " + excluded[test_name] 116 | except KeyError: 117 | return "Excluded" 118 | 119 | def run_details(self, test_name): 120 | details = [] 121 | self.append_diff(test_name, details) 122 | self.append_full_output(test_name, details) 123 | self.append_dmesg(test_name, details) 124 | return "".join(details) 125 | 126 | def append_diff(self, test_name, details): 127 | try: 128 | out_path = os.path.join(TESTS_DIR, test_name + ".out") 129 | with open(out_path, "r", errors="backslashreplace") as f: 130 | out = f.readlines() 131 | out_bad_path = os.path.join(RESULTS_DIR, test_name + ".out.bad") 132 | with open(out_bad_path, "r", errors="backslashreplace") as f: 133 | out_bad = f.readlines() 134 | except OSError: 135 | return 136 | 137 | diff = difflib.unified_diff(out, out_bad, out_path, out_bad_path) 138 | details.extend(diff) 139 | 140 | def append_full_output(self, test_name, details): 141 | full_path = os.path.join(RESULTS_DIR, test_name + ".full") 142 | try: 143 | # There are some absurdly large full results. 144 | if os.path.getsize(full_path) < 100_000: 145 | with open(full_path, "r", errors="backslashreplace") as f: 146 | if details: 147 | details.append("--\n") 148 | details.append(f"{full_path}:\n") 149 | details.append(f.read()) 150 | except OSError: 151 | pass 152 | 153 | def append_dmesg(self, test_name, details): 154 | dmesg_path = os.path.join(RESULTS_DIR, test_name + ".dmesg") 155 | try: 156 | with open(dmesg_path, "r", errors="backslashreplace") as f: 157 | if details: 158 | details.append("--\n") 159 | details.append(f"{dmesg_path}:\n") 160 | details.append(f.read()) 161 | except OSError: 162 | pass 163 | -------------------------------------------------------------------------------- /benchpress/example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | arg = sys.argv[1] 6 | 7 | if arg == 'generic': 8 | print("case1: PASS") 9 | print("case2 PASS") 10 | print("case3 FAIL") 11 | print("case4: FAIL") 12 | 13 | if arg == 'returncode': 14 | print() 15 | -------------------------------------------------------------------------------- /benchpress/poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | category = "dev" 3 | description = "Atomic file writes." 4 | name = "atomicwrites" 5 | optional = false 6 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 7 | version = "1.2.1" 8 | 9 | [[package]] 10 | category = "dev" 11 | description = "Classes Without Boilerplate" 12 | name = "attrs" 13 | optional = false 14 | python-versions = "*" 15 | version = "18.2.0" 16 | 17 | [[package]] 18 | category = "main" 19 | description = "Composable command line interface toolkit" 20 | name = "click" 21 | optional = false 22 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 23 | version = "7.0" 24 | 25 | [[package]] 26 | category = "dev" 27 | description = "Cross-platform colored terminal text." 28 | marker = "sys_platform == \"win32\"" 29 | name = "colorama" 30 | optional = false 31 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 32 | version = "0.4.1" 33 | 34 | [[package]] 35 | category = "dev" 36 | description = "Code coverage measurement for Python" 37 | name = "coverage" 38 | optional = false 39 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" 40 | version = "4.5.2" 41 | 42 | [[package]] 43 | category = "main" 44 | description = "A backport of the dataclasses module for Python 3.6" 45 | name = "dataclasses" 46 | optional = false 47 | python-versions = "*" 48 | version = "0.6" 49 | 50 | [[package]] 51 | category = "dev" 52 | description = "More routines for operating on iterables, beyond itertools" 53 | name = "more-itertools" 54 | optional = false 55 | python-versions = "*" 56 | version = "5.0.0" 57 | 58 | [package.dependencies] 59 | six = ">=1.0.0,<2.0.0" 60 | 61 | [[package]] 62 | category = "dev" 63 | description = "plugin and hook calling mechanisms for python" 64 | name = "pluggy" 65 | optional = false 66 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 67 | version = "0.8.1" 68 | 69 | [[package]] 70 | category = "dev" 71 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 72 | name = "py" 73 | optional = false 74 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 75 | version = "1.7.0" 76 | 77 | [[package]] 78 | category = "dev" 79 | description = "pyfakefs implements a fake file system that mocks the Python file system modules." 80 | name = "pyfakefs" 81 | optional = false 82 | python-versions = "*" 83 | version = "3.5.6" 84 | 85 | [[package]] 86 | category = "dev" 87 | description = "pytest: simple powerful testing with Python" 88 | name = "pytest" 89 | optional = false 90 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 91 | version = "4.1.1" 92 | 93 | [package.dependencies] 94 | atomicwrites = ">=1.0" 95 | attrs = ">=17.4.0" 96 | colorama = "*" 97 | more-itertools = ">=4.0.0" 98 | pluggy = ">=0.7" 99 | py = ">=1.5.0" 100 | setuptools = "*" 101 | six = ">=1.10.0" 102 | 103 | [[package]] 104 | category = "main" 105 | description = "YAML parser and emitter for Python" 106 | name = "pyyaml" 107 | optional = false 108 | python-versions = "*" 109 | version = "3.13" 110 | 111 | [[package]] 112 | category = "dev" 113 | description = "Python 2 and 3 compatibility utilities" 114 | name = "six" 115 | optional = false 116 | python-versions = ">=2.6, !=3.0.*, !=3.1.*" 117 | version = "1.12.0" 118 | 119 | [metadata] 120 | content-hash = "92c271cc7c9e7886d9e39bf178f375e9e35b69939c4614df408b0140851d6a35" 121 | python-versions = "^3.6" 122 | 123 | [metadata.hashes] 124 | atomicwrites = ["0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"] 125 | attrs = ["10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", "ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"] 126 | click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] 127 | colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] 128 | coverage = ["06123b58a1410873e22134ca2d88bd36680479fe354955b3579fb8ff150e4d27", "09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", "0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", "0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", "0d34245f824cc3140150ab7848d08b7e2ba67ada959d77619c986f2062e1f0e8", "10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", "1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", "1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", "258b21c5cafb0c3768861a6df3ab0cfb4d8b495eee5ec660e16f928bf7385390", "2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", "3ad59c84c502cd134b0088ca9038d100e8fb5081bbd5ccca4863f3804d81f61d", "447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", "46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", "4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", "510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", "5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", "5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", "5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", "6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", "6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", "71afc1f5cd72ab97330126b566bbf4e8661aab7449f08895d21a5d08c6b051ff", "7349c27128334f787ae63ab49d90bf6d47c7288c63a0a5dfaa319d4b4541dd2c", "77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", "828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", "859714036274a75e6e57c7bab0c47a4602d2a8cfaaa33bbdb68c8359b2ed4f5c", "85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", "869ef4a19f6e4c6987e18b315721b8b971f7048e6eaea29c066854242b4e98d9", "8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", "977e2d9a646773cc7428cdd9a34b069d6ee254fadfb4d09b3f430e95472f3cf3", "99bd767c49c775b79fdcd2eabff405f1063d9d959039c0bdd720527a7738748a", "a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", "aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", "ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", "b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", "bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", "c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", "d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", "d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", "da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", "ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", "ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"] 129 | dataclasses = ["454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f", "6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"] 130 | more-itertools = ["38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", "c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", "fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"] 131 | pluggy = ["8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", "980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"] 132 | py = ["bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", "e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"] 133 | pyfakefs = ["efe9c318b2a37ae498a555889684c30ccb6a1b06bd391cb3baf0eb5ba68e9062", "f04dc2ab8567b474563b82f3b4a63b7c28f83bf071948ce92de6045ac7270d21"] 134 | pytest = ["41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2", "c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be"] 135 | pyyaml = ["3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", "3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", "40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", "558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", "a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", "aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", "bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", "d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", "d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", "e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", "e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"] 136 | six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] 137 | -------------------------------------------------------------------------------- /benchpress/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "fb-benchpress" 3 | version = "0.1.0" 4 | description = "Facebook kernel team's test runner" 5 | authors = ["Vinnie Magro "] 6 | license = "BSD-3-Clause" 7 | packages = [ 8 | { include = "benchpress" }, 9 | ] 10 | readme = "README.md" 11 | homepage = "https://github.com/facebook/fbkutils" 12 | 13 | [tool.poetry.dependencies] 14 | python = "^3.6" 15 | pyyaml = "^3.13" 16 | dataclasses = "^0.6.0" 17 | click = "^7.0" 18 | 19 | [tool.poetry.dev-dependencies] 20 | coverage = "^4.5" 21 | pyfakefs = "^3.5" 22 | pytest = "^4.1" 23 | 24 | [tool.poetry.scripts] 25 | benchpress = 'benchpress.cli:benchpress' 26 | 27 | [build-system] 28 | requires = ["poetry>=0.12"] 29 | build-backend = "poetry.masonry.api" 30 | 31 | [flake8] 32 | max-line-length=80 33 | -------------------------------------------------------------------------------- /benchpress/suites.yml: -------------------------------------------------------------------------------- 1 | - name: example-generic 2 | runner: generic 3 | description: simple test that outputs line by line results 4 | path: ./example 5 | args: 6 | - generic 7 | - name: example-return 8 | runner: generic 9 | description: simple test that is just returncode 10 | path: ./example 11 | args: 12 | - returncode 13 | -------------------------------------------------------------------------------- /benchpress/tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import logging 10 | 11 | 12 | # Disables logging while running unit tests 13 | logging.disable(logging.CRITICAL) 14 | -------------------------------------------------------------------------------- /benchpress/tests/test_factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import unittest 10 | from abc import ABCMeta, abstractmethod 11 | 12 | from benchpress.lib.factory import BaseFactory 13 | 14 | 15 | # Class hierarchy that is used for testing the factory 16 | class Superclass(object, metaclass=ABCMeta): 17 | @abstractmethod 18 | def abstract(self): 19 | pass 20 | 21 | 22 | class Subclass1(Superclass): 23 | def abstract(self): 24 | pass 25 | 26 | 27 | class RegisteredClass(object): 28 | def abstract(self): 29 | pass 30 | 31 | 32 | Superclass.register(RegisteredClass) 33 | # End test classes 34 | 35 | 36 | class TestBaseFactory(unittest.TestCase): 37 | def setUp(self): 38 | self.factory = BaseFactory(Superclass) 39 | 40 | def test_register_nonsubclass(self): 41 | """Can't register a non-subclass""" 42 | # Dummy is not a subclass, so registering it should fail 43 | with self.assertRaises(AssertionError): 44 | 45 | class Dummy: 46 | pass 47 | 48 | self.factory.register("dummy", Dummy) 49 | 50 | def test_register(self): 51 | """Can register subclasses""" 52 | self.factory.register("subclass", Subclass1) 53 | self.factory.register("registered", RegisteredClass) 54 | 55 | def test_create_unregistered(self): 56 | """Can't create unregistered type""" 57 | with self.assertRaises(KeyError): 58 | self.factory.create("dummy") 59 | 60 | def test_create_registered(self): 61 | """Can create registered type""" 62 | self.factory.register("subclass", Subclass1) 63 | self.assertTrue(isinstance(self.factory.create("subclass"), Subclass1)) 64 | 65 | def test_registered_names(self): 66 | """Can get list of registered classes""" 67 | self.assertListEqual([], self.factory.registered_names) 68 | self.factory.register("default", Subclass1) 69 | self.assertCountEqual(["default"], self.factory.registered_names) 70 | self.factory.register("subclass", Subclass1) 71 | self.assertCountEqual(["default", "subclass"], self.factory.registered_names) 72 | 73 | 74 | if __name__ == "__main__": 75 | unittest.main() 76 | -------------------------------------------------------------------------------- /benchpress/tests/test_file_hook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import unittest 10 | from unittest.mock import MagicMock 11 | 12 | from benchpress.plugins.hooks import FileHook 13 | from pyfakefs import fake_filesystem, fake_filesystem_unittest 14 | 15 | 16 | class TestFileHook(fake_filesystem_unittest.TestCase): 17 | def setUp(self): 18 | self.setUpPyfakefs() 19 | self.os = fake_filesystem.FakeOsModule(self.fs) 20 | self.hook = FileHook() 21 | 22 | def test_directory_pre(self): 23 | """A directory is created during the before phase""" 24 | # make sure the dir didn't exist before and then exists afterwards 25 | self.assertFalse(self.fs.exists("/fake/dir")) 26 | self.hook.before([{"path": "/fake/dir", "type": "dir"}], MagicMock()) 27 | self.assertTrue(self.fs.isdir("/fake/dir")) 28 | 29 | def test_directory_pre_exists(self): 30 | """If a directory already exists, don't fail""" 31 | self.fs.create_dir("/fake/dir") 32 | self.hook.before([{"path": "/fake/dir", "type": "dir"}], MagicMock()) 33 | self.assertTrue(self.fs.isdir("/fake/dir")) 34 | 35 | def test_directory_pre_no_permissions(self): 36 | """No permission to create a directory raises an error""" 37 | self.fs.create_dir("/fake") 38 | # make the /fake directory readonly 39 | self.os.chmod("/fake", 0o444) 40 | with self.assertRaises(OSError): 41 | self.hook.before([{"path": "/fake/dir", "type": "dir"}], MagicMock()) 42 | 43 | def test_directory_post(self): 44 | """A directory is deleted during the after phase""" 45 | # make sure the dir existed before and then does not exist afterwards 46 | self.fs.create_dir("/fake/dir") 47 | self.hook.after([{"path": "/fake/dir", "type": "dir"}], MagicMock()) 48 | self.assertFalse(self.fs.exists("/fake/dir")) 49 | 50 | def test_file_pre(self): 51 | """A file is created during the before phase""" 52 | # make sure the file didn't exist before and then exists afterwards 53 | self.fs.create_dir("/fake") 54 | self.assertFalse(self.fs.exists("/fake/file")) 55 | self.hook.before([{"path": "/fake/file", "type": "file"}], MagicMock()) 56 | self.assertTrue(self.fs.isfile("/fake/file")) 57 | 58 | def test_file_post(self): 59 | """A file is deleted during the after phase""" 60 | # make sure the file existed before and then does not exist afterwards 61 | self.fs.create_dir("/fake") 62 | self.fs.create_file("/fake/file") 63 | self.hook.after([{"path": "/fake/file", "type": "file"}], MagicMock()) 64 | self.assertFalse(self.fs.exists("/fake/file")) 65 | 66 | 67 | if __name__ == "__main__": 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /benchpress/tests/test_generic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import unittest 10 | from unittest.mock import MagicMock 11 | 12 | from benchpress.lib.parser import TestCaseResult, TestStatus 13 | from benchpress.suites.generic import GenericSuite 14 | 15 | 16 | class TestGeneric(unittest.TestCase): 17 | def setUp(self): 18 | self.parser = GenericSuite(MagicMock()) 19 | 20 | def test_sample_output(self): 21 | """Can parse output from running generic single-line output format""" 22 | # sample output from running ltp fs tests 23 | output = """case1: PASS 24 | some 25 | case2: FAIL 26 | other 27 | case3 FAIL 28 | garbage intermixed 29 | case4 PASS 30 | """ 31 | results = list(self.parser.parse(output.split("\n"), [], 0)) 32 | self.maxDiff = None 33 | self.assertEqual( 34 | [ 35 | TestCaseResult(name="case1", status=TestStatus.PASSED), 36 | TestCaseResult(name="case2", status=TestStatus.FAILED), 37 | TestCaseResult(name="case3", status=TestStatus.FAILED), 38 | TestCaseResult(name="case4", status=TestStatus.PASSED), 39 | TestCaseResult( 40 | name="exec", 41 | status=TestStatus.PASSED, 42 | details="""stdout: 43 | case1: PASS 44 | some 45 | case2: FAIL 46 | other 47 | case3 FAIL 48 | garbage intermixed 49 | case4 PASS 50 | 51 | 52 | stderr: 53 | """, 54 | ), 55 | ], 56 | results, 57 | ) 58 | 59 | 60 | if __name__ == "__main__": 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /benchpress/tests/test_ltp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import unittest 10 | from unittest.mock import MagicMock 11 | 12 | from benchpress.lib.parser import TestCaseResult, TestStatus 13 | from benchpress.suites.ltp import LtpSuite 14 | 15 | 16 | class TestLtp(unittest.TestCase): 17 | def setUp(self): 18 | self.parser = LtpSuite(MagicMock()) 19 | 20 | def test_sample_output(self): 21 | """Can parse output from running ltp fs tests""" 22 | # sample output from running ltp fs tests 23 | output = """INFO: creating /root/ltp/output directory 24 | INFO: creating /root/ltp/results directory 25 | Checking for required user/group ids 26 | 27 | 'nobody' user id and group found. 28 | 'bin' user id and group found. 29 | 'daemon' user id and group found. 30 | Users group found. 31 | Sys group found. 32 | Required users/groups exist. 33 | If some fields are empty or look unusual you may have an old version. 34 | Compare to the current minimal requirements in Documentation/Changes. 35 | 36 | <<>> 37 | tag=gf14 stime=1548867875 38 | cmdline="growfiles -W gf14 -b -e 1 -u -i 0 -L 20 -w -l -C 1 -T 10 -f glseek19 -S 2 -d $TMPDIR" 39 | contacts="" 40 | analysis=exit 41 | <<>> 42 | gf14 1 TBROK : Test passed 43 | <<>> 44 | initiation_status="ok" 45 | duration=15 termination_type=exited termination_id=0 corefile=no 46 | cutime=535 cstime=844 47 | <<>> 48 | <<>> 49 | tag=gf15 stime=1548867890 50 | cmdline="growfiles -W gf15 -b -e 1 -u -r 1-49600 -I r -u -i 0 -L 120 -f Lgfile1 -d $TMPDIR" 51 | contacts="" 52 | analysis=exit 53 | <<>> 54 | gf15 1 TPASS : Test passed 55 | gf15 2 TPASS : Test passed 56 | <<>> 57 | initiation_status="ok" 58 | duration=19 termination_type=exited termination_id=0 corefile=no 59 | cutime=1673 cstime=212 60 | <<>> 61 | <<>> 62 | tag=gf16 stime=1548867909 63 | cmdline="growfiles -W gf16 -b -e 1 -i 0 -L 120 -u -g 4090 -T 101 -t 408990 -l -C 10 -c 1000 -S 10 -f Lgf02_ -d $TMPDIR" 64 | contacts="" 65 | analysis=exit 66 | <<>> 67 | gf16 1 TFAIL : Test passed 68 | <<>> 69 | initiation_status="ok" 70 | duration=121 termination_type=exited termination_id=0 corefile=no 71 | cutime=10074 cstime=1922 72 | <<>> 73 | <<>> 74 | tag=quota_remount_test01 stime=1548866311 75 | cmdline="quota_remount_test01.sh" 76 | tag=gf16 stime=1548867909 77 | cmdline="growfiles -W gf16 -b -e 1 -i 0 -L 120 -u -g 4090 -T 101 -t 408990 -l -C 10 -c 1000 -S 10 -f Lgf02_ -d $TMPDIR" 78 | contacts="" 79 | analysis=exit 80 | <<>> 81 | incrementing stop 82 | quota_remount_test01 0 TINFO : Successfully mounted the File System 83 | quota_remount_test01 0 TINFO : Successfully Created Quota Files 84 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.group on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 85 | quotaon: Quota format not supported in kernel. 86 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.user on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 87 | quotaon: Quota format not supported in kernel. 88 | Could not turn quota on 89 | quota_remount_test01 1 TFAIL : ltpapicmd.c:188: Quota on Remount Failed 90 | gf16 1 TFAIL : Test passed 91 | <<>> 92 | initiation_status="ok" 93 | duration=1 termination_type=exited termination_id=2 corefile=no 94 | cutime=1 cstime=4 95 | duration=121 termination_type=exited termination_id=0 corefile=no 96 | cutime=10074 cstime=1922 97 | <<>> 98 | """ 99 | results = list(self.parser.parse(output.split("\n"), None, 0)) 100 | self.maxDiff = None 101 | self.assertEqual( 102 | [ 103 | TestCaseResult( 104 | name="gf14_1", 105 | status=TestStatus.FAILED, 106 | details=""" 107 | gf14 1 TBROK : Test passed 108 | """.strip(), 109 | ), 110 | TestCaseResult( 111 | name="gf15_1", 112 | status=TestStatus.PASSED, 113 | details=""" 114 | gf15 1 TPASS : Test passed 115 | gf15 2 TPASS : Test passed 116 | """.strip(), 117 | ), 118 | TestCaseResult( 119 | name="gf15_2", 120 | status=TestStatus.PASSED, 121 | details=""" 122 | gf15 1 TPASS : Test passed 123 | gf15 2 TPASS : Test passed 124 | """.strip(), 125 | ), 126 | TestCaseResult( 127 | name="gf16_1", 128 | status=TestStatus.FAILED, 129 | details=""" 130 | gf16 1 TFAIL : Test passed 131 | """.strip(), 132 | ), 133 | TestCaseResult( 134 | name="quota_remount_test01_1", 135 | status=TestStatus.FAILED, 136 | details=""" 137 | incrementing stop 138 | quota_remount_test01 0 TINFO : Successfully mounted the File System 139 | quota_remount_test01 0 TINFO : Successfully Created Quota Files 140 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.group on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 141 | quotaon: Quota format not supported in kernel. 142 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.user on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 143 | quotaon: Quota format not supported in kernel. 144 | Could not turn quota on 145 | quota_remount_test01 1 TFAIL : ltpapicmd.c:188: Quota on Remount Failed 146 | gf16 1 TFAIL : Test passed 147 | """.strip(), 148 | ), 149 | TestCaseResult( 150 | name="gf16_1", 151 | status=TestStatus.FAILED, 152 | details=""" 153 | incrementing stop 154 | quota_remount_test01 0 TINFO : Successfully mounted the File System 155 | quota_remount_test01 0 TINFO : Successfully Created Quota Files 156 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.group on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 157 | quotaon: Quota format not supported in kernel. 158 | quotaon: using /tmp/ltp-gR1S51MtVi/mnt/aquota.user on /dev/loop2 [/tmp/ltp-gR1S51MtVi/mnt]: No such process 159 | quotaon: Quota format not supported in kernel. 160 | Could not turn quota on 161 | quota_remount_test01 1 TFAIL : ltpapicmd.c:188: Quota on Remount Failed 162 | gf16 1 TFAIL : Test passed 163 | """.strip(), 164 | ), 165 | ], 166 | results, 167 | ) 168 | 169 | 170 | if __name__ == "__main__": 171 | unittest.main() 172 | -------------------------------------------------------------------------------- /benchpress/tests/test_packetdrill.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2018-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import unittest 10 | from unittest.mock import MagicMock 11 | 12 | from benchpress.lib.parser import TestCaseResult, TestStatus 13 | from benchpress.suites.packetdrill import PacketdrillSuite 14 | from pyfakefs import fake_filesystem_unittest 15 | 16 | 17 | class TestPacketdrill(fake_filesystem_unittest.TestCase): 18 | def setUp(self): 19 | self.setUpPyfakefs() 20 | self.parser = PacketdrillSuite(MagicMock()) 21 | 22 | def test_output(self): 23 | stdout = ["001-passed 0", "002-failed 1"] 24 | results = self.parser.parse(stdout, [], 0) 25 | self.assertEqual( 26 | [ 27 | TestCaseResult(name="001-passed", status=TestStatus.PASSED), 28 | TestCaseResult(name="002-failed", status=TestStatus.FAILED), 29 | ], 30 | results, 31 | ) 32 | 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /benchpress/tests/test_shell_hook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import os 10 | import subprocess 11 | import unittest 12 | from unittest.mock import patch 13 | 14 | from benchpress.plugins.hooks.shell import ShellHook 15 | from pyfakefs import fake_filesystem_unittest 16 | 17 | 18 | FAKEDIR = "/tmp_benchpress_test" 19 | 20 | 21 | class TestShellHook(fake_filesystem_unittest.TestCase): 22 | def setUp(self): 23 | self.setUpPyfakefs() 24 | self.original_dir = os.getcwd() 25 | self.hook = ShellHook() 26 | 27 | self.fs.create_dir(FAKEDIR) 28 | 29 | def tearDown(self): 30 | os.chdir(self.original_dir) 31 | 32 | def test_cd_pre(self): 33 | """Can cd to change the working directory of a test""" 34 | self.assertNotEqual(FAKEDIR, os.getcwd()) 35 | self.assertEqual(self.original_dir, os.getcwd()) 36 | self.hook.before({"before": ["cd {}".format(FAKEDIR)]}) 37 | self.assertEqual(FAKEDIR, os.getcwd()) 38 | 39 | def test_cd_pre_reset(self): 40 | """cd in a before hook is reset in post""" 41 | self.assertNotEqual(FAKEDIR, os.getcwd()) 42 | self.assertEqual(self.original_dir, os.getcwd()) 43 | self.hook.before({"before": ["cd {}".format(FAKEDIR)]}) 44 | self.assertEqual(FAKEDIR, os.getcwd()) 45 | self.hook.after({"before": ["cd {}".format(FAKEDIR)]}) 46 | self.assertEqual(self.original_dir, os.getcwd()) 47 | 48 | @patch("subprocess.check_call") 49 | def test_pre_subprocess(self, check_call): 50 | """pre hook executes commands""" 51 | self.hook.before({"before": ['echo "hello world" extra']}) 52 | check_call.assert_called_once_with( 53 | 'echo "hello world" extra', 54 | shell=True, 55 | stdout=subprocess.DEVNULL, 56 | stderr=subprocess.DEVNULL, 57 | ) 58 | 59 | @patch("subprocess.check_call") 60 | def test_post_subprocess(self, check_call): 61 | """post hook executes commands""" 62 | self.hook.after({"after": ['echo "hello world" extra']}) 63 | check_call.assert_called_once_with( 64 | 'echo "hello world" extra', 65 | shell=True, 66 | stdout=subprocess.DEVNULL, 67 | stderr=subprocess.DEVNULL, 68 | ) 69 | 70 | @patch("subprocess.check_call") 71 | def test_subprocess_fail(self, check_call): 72 | """Failing subprocess raises error""" 73 | with self.assertRaises(subprocess.CalledProcessError): 74 | check_call.side_effect = subprocess.CalledProcessError(1, "") 75 | self.hook.before({"before": ["true"]}) 76 | 77 | 78 | if __name__ == "__main__": 79 | unittest.main() 80 | -------------------------------------------------------------------------------- /benchpress/tests/test_suite.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import io 10 | import os 11 | import subprocess 12 | import sys 13 | import tempfile 14 | import unittest 15 | from unittest.mock import MagicMock, Mock, call 16 | 17 | from benchpress.lib.hook_factory import HookFactory 18 | from benchpress.lib.parser import TestCaseResult, TestStatus 19 | from benchpress.suites import Suite 20 | 21 | 22 | HookFactory.create = MagicMock() 23 | 24 | 25 | class TestSuite(unittest.TestCase): 26 | def setUp(self): 27 | self.suite_config = {"name": "test", "description": "desc", "args": []} 28 | self.mock_hook = MagicMock() 29 | HookFactory.create.return_value = self.mock_hook 30 | Suite.parse = Mock() 31 | 32 | def test_arg_list(self): 33 | """Argument list is formatted correctly with lists or dicts""" 34 | self.assertListEqual( 35 | ["--output-format=json", "a"], Suite.arg_list(["--output-format=json", "a"]) 36 | ) 37 | 38 | expected = ["--output-format", "json", "--file"] 39 | actual = Suite.arg_list({"output-format": "json", "file": None}) 40 | # items are the same regardless of order 41 | self.assertCountEqual(expected, actual) 42 | # '--output-format' comes immediately before 'json' 43 | self.assertEqual(actual.index("--output-format") + 1, actual.index("json")) 44 | 45 | def test_run_succeed(self): 46 | """Echo is able to run and be parsed correctly 47 | 48 | Run a suite to echo some json and make sure it can be parse and is 49 | exported correctly.""" 50 | mock_data = '{"key": "hello"}' 51 | self.suite_config["args"] = [mock_data] 52 | self.suite_config["metrics"] = ["key"] 53 | 54 | self.suite_config["path"] = "echo" 55 | 56 | suite = Suite(self.suite_config) 57 | suite.parse = Mock( 58 | return_value=[TestCaseResult(name="key", status=TestStatus.PASSED)] 59 | ) 60 | 61 | metrics = suite.run() 62 | suite.parse.assert_called_with([mock_data], [], 0) 63 | self.assertEqual( 64 | [TestCaseResult(name="key", status=TestStatus.PASSED)], metrics 65 | ) 66 | 67 | def test_run_fail(self): 68 | """Exit 1 raises an exception""" 69 | self.suite_config["args"] = ["-c", 'echo "error" >&2; exit 1'] 70 | 71 | self.suite_config["path"] = "sh" 72 | 73 | suite = Suite(self.suite_config) 74 | 75 | with self.assertRaises(subprocess.CalledProcessError) as e: 76 | suite.run() 77 | e = e.exception 78 | self.assertEqual("", e.stdout.strip()) 79 | self.assertEqual("error", e.stderr.strip()) 80 | 81 | def test_run_fail_no_check_returncode(self): 82 | """Bad return code doesn't fail when check_returncode is False""" 83 | self.suite_config["args"] = ["-c", 'echo "error" >&2; exit 1'] 84 | 85 | self.suite_config["path"] = "sh" 86 | self.suite_config["check_returncode"] = False 87 | 88 | suite = Suite(self.suite_config) 89 | 90 | # suite.run won't raise an exception 91 | suite.run() 92 | 93 | def test_run_no_binary(self): 94 | """Nonexistent binary raises an error""" 95 | self.suite_config["path"] = "somethingthatdoesntexist" 96 | self.suite_config["metrics"] = [] 97 | 98 | suite = Suite(self.suite_config) 99 | 100 | with self.assertRaises(OSError): 101 | suite.run() 102 | 103 | def test_run_parser_error(self): 104 | """A crashed parser raises an error""" 105 | self.suite_config["path"] = "true" 106 | self.suite_config["metrics"] = [] 107 | 108 | suite = Suite(self.suite_config) 109 | suite.parse = Mock(side_effect=ValueError("")) 110 | 111 | with self.assertRaises(ValueError): 112 | suite.run() 113 | 114 | def test_run_timeout(self): 115 | """Binary running past timeout raises an error""" 116 | self.suite_config["timeout"] = 0.1 117 | self.suite_config["path"] = "/bin/sh" 118 | self.suite_config["args"] = ["-c", "yes"] 119 | 120 | suite = Suite(self.suite_config) 121 | 122 | with self.assertRaises(subprocess.TimeoutExpired): 123 | suite.run() 124 | 125 | def test_run_timeout_is_pass(self): 126 | """Binary running past timeout raises an error""" 127 | self.suite_config["timeout"] = 0.1 128 | self.suite_config["timeout_is_pass"] = True 129 | self.suite_config["path"] = "/bin/sh" 130 | self.suite_config["args"] = [ 131 | "-c", 132 | 'echo "wow" && echo "err" > /dev/stderr && sleep 2', 133 | ] 134 | 135 | suite = Suite(self.suite_config) 136 | 137 | suite.run() 138 | 139 | suite.parse.assert_called_with(["timed out as expected"], [], 0) 140 | 141 | def test_tee_stdouterr(self): 142 | """tee_output option works correctly 143 | 144 | With tee_option=True, the suite should print the subprocess stdout lines 145 | starting with 'stdout:' and stderr starting with 'stderr:'""" 146 | mock_data = "line 1 from echo\nthis is the second line" 147 | self.suite_config["args"] = [mock_data] 148 | self.suite_config["metrics"] = ["key"] 149 | self.suite_config["tee_output"] = True 150 | 151 | self.suite_config["path"] = "echo" 152 | 153 | suite = Suite(self.suite_config) 154 | # capture stdout/err 155 | orig_stdout, orig_stderr = sys.stdout, sys.stderr 156 | sys.stdout = io.StringIO() 157 | sys.stderr = io.StringIO() 158 | 159 | suite.run() 160 | 161 | expected = "stdout: line 1 from echo\nstdout: this is the second line\n" 162 | self.assertEqual(sys.stdout.getvalue(), expected) 163 | 164 | # test with stderr and stdout 165 | # first reset stdout string 166 | sys.stdout.truncate(0) 167 | sys.stdout.seek(0) 168 | 169 | self.suite_config["path"] = "sh" 170 | self.suite_config["args"] = ["-c", 'echo "error" >&2 && echo "from stdout"'] 171 | self.suite_config["tee_output"] = True 172 | 173 | suite = Suite(self.suite_config) 174 | suite.run() 175 | 176 | expected = "stdout: from stdout\nstderr: error\n" 177 | self.assertEqual(sys.stdout.getvalue(), expected) 178 | 179 | sys.stdout = orig_stdout 180 | sys.stderr = orig_stderr 181 | 182 | def test_tee_output_file(self): 183 | """tee_output can write to file.""" 184 | mock_data = "line 1 from echo\nthis is the second line" 185 | self.suite_config["args"] = [mock_data] 186 | self.suite_config["metrics"] = ["key"] 187 | 188 | fd, teefile = tempfile.mkstemp() 189 | os.close(fd) 190 | 191 | self.suite_config["path"] = "sh" 192 | self.suite_config["args"] = ["-c", 'echo "error" >&2 && echo "from stdout"'] 193 | self.suite_config["tee_output"] = teefile 194 | 195 | suite = Suite(self.suite_config) 196 | suite.run() 197 | 198 | expected = "stdout: from stdout\nstderr: error\n" 199 | with open(teefile, "r") as tmp: 200 | self.assertEqual(tmp.read(), expected) 201 | os.remove(teefile) 202 | 203 | def test_hooks(self): 204 | """Suite runs hooks before/after in stack order""" 205 | self.suite_config["path"] = "true" 206 | self.suite_config["hooks"] = [ 207 | {"hook": "first", "options": {"a": 1}}, 208 | {"hook": "second", "options": {"b": 1}}, 209 | ] 210 | mock = MagicMock() 211 | first = mock.first 212 | second = mock.second 213 | 214 | def get_mock_hook(name): 215 | if name == "first": 216 | return first 217 | else: 218 | return second 219 | 220 | HookFactory.create.side_effect = get_mock_hook 221 | 222 | suite = Suite(self.suite_config) 223 | suite.run() 224 | 225 | self.assertListEqual( 226 | [ 227 | call.first.before({"a": 1}, suite), 228 | call.second.before({"b": 1}, suite), 229 | # post hooks run in reverse order 230 | call.second.after({"b": 1}, suite), 231 | call.first.after({"a": 1}, suite), 232 | ], 233 | mock.method_calls, 234 | ) 235 | 236 | 237 | if __name__ == "__main__": 238 | unittest.main() 239 | -------------------------------------------------------------------------------- /benchpress/tests/test_xfstests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2017-present, Facebook, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed under the BSD-style license found in the 6 | # LICENSE file in the root directory of this source tree. An additional grant 7 | # of patent rights can be found in the PATENTS file in the same directory. 8 | 9 | import os.path 10 | import unittest 11 | from unittest.mock import MagicMock 12 | 13 | from benchpress.lib.parser import TestCaseResult, TestStatus 14 | from benchpress.suites.xfstests import RESULTS_DIR, TESTS_DIR, XfstestsSuite 15 | from pyfakefs import fake_filesystem_unittest 16 | 17 | 18 | class TestXfstests(fake_filesystem_unittest.TestCase): 19 | def setUp(self): 20 | self.setUpPyfakefs() 21 | self.parser = XfstestsSuite(MagicMock()) 22 | self.fs.create_dir(TESTS_DIR) 23 | self.fs.create_dir(os.path.join(TESTS_DIR, "generic")) 24 | self.fs.create_dir(RESULTS_DIR) 25 | self.fs.create_dir(os.path.join(RESULTS_DIR, "generic")) 26 | 27 | def test_pass(self): 28 | stdout = ["generic/001 2s", "generic/002 2s ... 4s"] 29 | results = list(self.parser.parse(stdout, [], 0)) 30 | self.assertEqual( 31 | [ 32 | TestCaseResult( 33 | name="generic/001", status=TestStatus.PASSED, runtime=2.0 34 | ), 35 | TestCaseResult( 36 | name="generic/002", status=TestStatus.PASSED, runtime=4.0 37 | ), 38 | ], 39 | results, 40 | ) 41 | 42 | def test_output_mismatch(self): 43 | out = os.path.join(TESTS_DIR, "generic", "305.out") 44 | with open(out, "w") as f: 45 | f.write("expected\n") 46 | out_bad = os.path.join(RESULTS_DIR, "generic", "305.out.bad") 47 | with open(out_bad, "w") as f: 48 | f.write("actual\n") 49 | 50 | diff = f"""\ 51 | --- {TESTS_DIR}/generic/305.out 52 | +++ {RESULTS_DIR}/generic/305.out.bad 53 | @@ -1 +1 @@ 54 | -expected 55 | +actual 56 | """ 57 | 58 | stdout = [ 59 | "generic/305 output mismatch blah blah", 60 | "something something", 61 | "more output blah blah", 62 | ] 63 | results = list(self.parser.parse(stdout, [], 0)) 64 | self.assertEqual( 65 | [ 66 | TestCaseResult( 67 | name="generic/305", status=TestStatus.FAILED, details=diff 68 | ) 69 | ], 70 | results, 71 | ) 72 | 73 | def test_not_run(self): 74 | notrun1 = os.path.join(RESULTS_DIR, "generic", "001.notrun") 75 | with open(notrun1, "w") as f: 76 | f.write("fire walk with me\n") 77 | notrun2 = os.path.join(RESULTS_DIR, "generic", "002.notrun") 78 | with open(notrun2, "w") as f: 79 | f.write("black lodge\n") 80 | 81 | stdout = [ 82 | "generic/001 [not run] foo", 83 | "generic/002 0s ... [not run] bar", 84 | ] 85 | results = list(self.parser.parse(stdout, [], 0)) 86 | self.assertEqual( 87 | [ 88 | TestCaseResult( 89 | name="generic/001", 90 | status=TestStatus.SKIPPED, 91 | details="Not run: fire walk with me", 92 | ), 93 | TestCaseResult( 94 | name="generic/002", 95 | status=TestStatus.SKIPPED, 96 | details="Not run: black lodge", 97 | ), 98 | ], 99 | results, 100 | ) 101 | 102 | def test_expunged(self): 103 | with open("exclude_list", "w") as f: 104 | f.write("generic/001 # this is the water\n") 105 | 106 | stdout = ["generic/001 [expunged]"] 107 | results = list(self.parser.parse(stdout, [], 0)) 108 | self.assertEqual( 109 | [ 110 | TestCaseResult( 111 | name="generic/001", 112 | status=TestStatus.OMITTED, 113 | details="Excluded: this is the water", 114 | ) 115 | ], 116 | results, 117 | ) 118 | 119 | def test_invalid_unicode(self): 120 | full = os.path.join(RESULTS_DIR, "generic", "001.full") 121 | with open(full, "wb") as f: 122 | f.write(b"This is not valid UTF-8\xff\n") 123 | 124 | stdout = ["generic/001 filesystem is inconsistent"] 125 | results = list(self.parser.parse(stdout, [], 0)) 126 | self.assertEqual( 127 | [ 128 | TestCaseResult( 129 | name="generic/001", 130 | status=TestStatus.FAILED, 131 | details=f"{full}:\nThis is not valid UTF-8\\xff\n", 132 | ) 133 | ], 134 | results, 135 | ) 136 | 137 | 138 | if __name__ == "__main__": 139 | unittest.main() 140 | -------------------------------------------------------------------------------- /bsdacctd/Makefile: -------------------------------------------------------------------------------- 1 | binary = bsdacctd 2 | obj = bsdacctd.o 3 | all: $(binary) 4 | 5 | $(binary): $(obj) 6 | gcc $(obj) -o $@ 7 | 8 | %.o: %.c 9 | gcc -D_GNU_SOURCE -O2 -Wall -Wextra -fno-strict-aliasing $< -c -o $@ 10 | 11 | clean: 12 | rm -f $(obj) $(binary) 13 | -------------------------------------------------------------------------------- /bsdacctd/packman.yml: -------------------------------------------------------------------------------- 1 | packages: 2 | fb-bsdacctd: 3 | packager: kernel 4 | summary: BSDv3 Accounting in Userspace 5 | rules: 6 | buck:fbkutils/bsdacctd:bsdacctd: 7 | fbkutils/bsdacctd/bsdacctd: 8 | path: /usr/local/bin/bsdacctd 9 | -------------------------------------------------------------------------------- /ncrx/README.md: -------------------------------------------------------------------------------- 1 | ncrx is now in the [netconsd repository](https://github.com/facebook/fbkutils). 2 | -------------------------------------------------------------------------------- /netconsd/README.md: -------------------------------------------------------------------------------- 1 | netconsd is now in the [netconsd repository](https://github.com/facebook/fbkutils). 2 | -------------------------------------------------------------------------------- /netesto/Netesto.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookarchive/fbkutils/676b68089ffff7c3c8e6872bcbcf5d6f404eb603/netesto/Netesto.pdf -------------------------------------------------------------------------------- /netesto/README: -------------------------------------------------------------------------------- 1 | NOTE: Things should be working fine now. Please let me know (netesto@brakmo.org) 2 | if you encounter any issues. 3 | 4 | Netesto is a suite of tools for running multi-host network experiments that 5 | supports the collection and display of relevant data and statistics. It 6 | currently supports TCP RPC and STREAM transfers through netperf, with other 7 | type of transfers planned for the future (i.e. neper, etc.) In addition to running 8 | the netperf transfers, it also runs ping to collect RTT information and it also 9 | collects TCP cwnd and rtt for each of the flows as well as retransmits and cpu 10 | load for each of the hosts. 11 | 12 | When running netesto tests, one machine acts as the controller which 13 | communicates with the machines doing the actual sends (test clients) and receives 14 | (test servers). The controller starts the transfers between test clients and 15 | test servers and collects the experiment's data. This data is processed and 16 | put into a csv file in the controller, the raw data is also stored in the 17 | controller. In order to achieve this, the files in the netesto-remote directory 18 | must be copied first to the clients/servers and the netesto daemon must be 19 | started from the directory containing these files. 20 | 21 | However, before starting netesto as a daemon you need to: 22 | 1) Make sure netperf and netsever are installed in your system 23 | 2) Check that ss is also installed (install if necessary) 24 | 3) If necessary install gs (ghostscript). It is used to create the graphs, 25 | such as rates, cwnd, RTTs, etc. 26 | 3) Modify the file "clients.txt" and add the IPv6 address of the controller. 27 | Note that I have only used netesto in IPv6 networks and it probably will 28 | not work as is on IPv4 networks. 29 | The file "clients.txt" contains a whitelist of machines that are allowed 30 | to connect to the netesto daemons. 31 | 32 | The netesto daemon can then be started by executing the command: 33 | 34 | ./netesto.py -s [--debug=] & 35 | 36 | Another set of files, under the netesto-local directory must be copied to 37 | the controller. I like to create a new directory for each new set of experiments. 38 | In my case, I use ~/Exp as my main netesto directory, and under it I will create 39 | subdirectories for new sets of experiments. For example, I may create ~/Exp/bbr 40 | to run BBR tests. To get started, ~/Exp/tests may be more appropriately. 41 | 42 | A set of tests can be started by the controller by specifying a particular 43 | netesto script: 44 | 45 | ./netesto.py < script.experiment 46 | 47 | For example, the following script starts one TCP RR netperf flow using 1MB 48 | requests and 1 byte replies lasting 60 seconds using TCP Cubic. 49 | 50 | # Run one Request/Reply experiment 51 | HOST_SUFFIX dc1.mynetwork.com 52 | SOURCE inlib 53 | SET client=hostname1 54 | SET server=hostname2 55 | RUN MServerRR servers=$server clients=$client expName=1c1s1fr ca=cubic dur=60 \ 56 | instances=1 reqs=1M reply=1 57 | 58 | The script script.test already contains these commands, you just need to replace 59 | the approriate values (dc1.mynetwork.com and hostname1, hostname2 in this case). 60 | 61 | Netesto reads its commands from standard input. So if the file script.test contains 62 | the above commands, you could run it with: 63 | 64 | ./netesto.py [-d | --debug=] < script.test 65 | 66 | This will create a new subdirectory for the test results. These subdirectories use 67 | numbers as names, and not always increase monotonically. If you look under this 68 | subdirectory there will be the file "exp.html" containing some results and graphs. 69 | 70 | The best way to understand the netesto's grammar is to look a the script files (script.*) 71 | and the libraries (inlib*) provided. 72 | 73 | A more interesting script is "script.basic" which runs more than 1 test. Edit the file 74 | appropriately and then you can run it by: 75 | 76 | ./netesto.py [-d | --debug=] < script.basic 77 | 78 | As mentioned earlier, each test/experiment will create a new numeric directory on the 79 | controller containing information about the experiment as well as some graphs created 80 | by the netesto command PROCESS_EXP (automatically called by all library macros) and a 81 | new lines are appended to exp.csv containing experimental results. 82 | 83 | The directory "exp" under the local directory can be used to create HTML files containing 84 | tables and graphs. For example, to create an HTML file containing a table of all the 85 | experiments we have run so far (assuming the current directory is names tests) you 86 | can do the following: 87 | 88 | cd exp 89 | ./exp.py --file=exp.script.basic > ../basic.html 90 | cd .. 91 | 92 | Then opening the file "basic.html" in your browser will show a table of all the 93 | tests ran so far. Clicking an experiment under the "exp" column will open the 94 | file (exp.html) for that test/experiment. 95 | 96 | In some cases you may want to share the data with others, or you may want to 97 | look at the html files on another host. The script "collectExp.sh" will package 98 | the csv file and the main files under each experiment and create exp.tgz. Note 99 | that it will put all the files under a directory with the current directory 100 | name. You can then copy this file to another machine. 101 | 102 | For other more interesting example of exp.py scripts, I've included some 103 | experimental results under exp/bbr3.tgz and two sample scripts. The first 104 | script creates a table of all experiments: 105 | 106 | # Sample file to create a table of all experiments. 107 | # Run tar -zxf bbr3.tgz first to create sample data 108 | # 109 | # Usage: ./exp.py --file=exp.script.all.example > example.all.html 110 | # 111 | # default number of decimal digits to show in tables 112 | format = 2 113 | 114 | # Use log command to specify a file where to write log/debug info 115 | #log = out.log 116 | 117 | # Where the csv file is located (bbr3/exp.csv in this case). 118 | # This command can only be followed by the "format" or "log" commands 119 | File = bbr3/exp 120 | 121 | doTable 122 | end 123 | 124 | One can then use this script for creating a table of all the experiments as follows: 125 | 126 | cd exp 127 | tar -zxf bbr3.tgz 128 | ./exp.py --file=exp.script.all.example > example.all.html 129 | 130 | The file exp.script.example contanins a more interesing script that outputs tables of 131 | a subset of experiments and also does some bar graphs. 132 | 133 | Note that netesto has grown organically from basic functionality to its present level. 134 | That is, it does not follow an overall design. Instead, it started with basic functionality 135 | and it has grown over time. The code is not well documented, but I am working on 136 | restructuring and adding documentation. Please let me know of any issues you encounter. 137 | There are many things I plan to improve (little by little). 138 | 139 | For more information read Netesto.pdf and look at the sample libraries (inlib*) 140 | and sample test scripts (script.*) in netesto/local directory 141 | 142 | Author: Lawrence Brakmo, netesto@brakmo.org 143 | -------------------------------------------------------------------------------- /netesto/local/collectExp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Given experiment directories as arguments, this script will copy 4 | # the most important parts to a subdirectory within the "Small" directory 5 | # and then tar them. 6 | # This simplifies copying to another machine. 7 | # 8 | # Don't forget to also copy exp.html and exp.csv 9 | # 10 | if ! [ -a Small ] ; then 11 | mkdir Small 12 | fi 13 | 14 | p=`pwd` 15 | d=${p##*/} 16 | if ! [ -a Small/$d ] ; then 17 | mkdir Small/$d 18 | fi 19 | 20 | rm -fR Small/$d/* 21 | 22 | args="$@" 23 | for a in $args ; do 24 | mkdir Small/$d/$a 25 | cp $a/*.html Small/$d/$a 26 | cp $a/*.jpg Small/$d/$a 27 | done 28 | 29 | cd Small 30 | cp ../*.html ../*.csv $d 31 | tar -zcf ../exp.tgz $d 32 | cd .. 33 | 34 | -------------------------------------------------------------------------------- /netesto/local/exp/bbr3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookarchive/fbkutils/676b68089ffff7c3c8e6872bcbcf5d6f404eb603/netesto/local/exp/bbr3.tgz -------------------------------------------------------------------------------- /netesto/local/exp/exp.script.all.example: -------------------------------------------------------------------------------- 1 | # Sample file to create a table of all experiments. 2 | # Run tar -zxf bbr3.tgz first to create sample data 3 | # 4 | # Usage: ./exp.py --file=exp.script.all.example > example.all.html 5 | # 6 | # default number of decimal digits to show in tables 7 | format = 2 8 | 9 | # Use log command to specify a file where to write log/debug info 10 | #log = out.log 11 | 12 | # Where the csv file is located (bbr3/exp.csv in this case). 13 | # The only commands allowed before this one are the "format" or "log" commands 14 | File = bbr3/exp 15 | 16 | doTable 17 | end 18 | -------------------------------------------------------------------------------- /netesto/local/exp/exp.script.basic: -------------------------------------------------------------------------------- 1 | # Sample file to create table of all experiments ran with 2 | # the script.basic experiment 3 | # 4 | # Usage: ./exp.py --file=exp.script.basic > basic.html 5 | # 6 | 7 | # default number of decimal digits to show in tables 8 | format = 2 9 | 10 | # Use log command to specify a file where to write log/debug info 11 | #log = out.log 12 | 13 | # Where the csv file is located (../exp.csv in this case). 14 | # The only commands allowed before this one are "format" or "log" commands 15 | File = ../exp 16 | 17 | # Relative (to the .html file) path where experiment directories are located 18 | # Used to find the experiment html files when the exp field is clicked 19 | relPath = . 20 | 21 | doTable 22 | end 23 | -------------------------------------------------------------------------------- /netesto/local/exp/exp.script.example: -------------------------------------------------------------------------------- 1 | # Sample file to create tables and plots of some experiments. 2 | # Run tar -zxf bbr3.tgz first to create the sample data 3 | # You also need to install gs (ghostscript) in order to create 4 | # graphs/plots. 5 | # 6 | # Usage: ./exp.py --file=exp.script.example > example.html 7 | # 8 | 9 | # default number of decimal digits to show in tables 10 | format = 2 11 | 12 | # Use log command to specify a file where to write log/debug info 13 | #log = out.log 14 | 15 | # Where the csv file is located (bbr3/exp.csv in this case). 16 | # The only commands allowed before this one are the "format" or "log" commands 17 | File = bbr3/exp 18 | 19 | # Select rows to use. Each experiment/test creates multiple rows 20 | # The row with ".0" in its "exp" field is the aggregate row 21 | Select = exp[has].0,testType==3Stream1,netem==10,bw==100mbit 22 | 23 | # Create fairness column from existing columns and/or numbers 24 | NewCol = fairness = rateMax / rateMin 25 | 26 | # Select columns to use 27 | Cols = exp,expName,test,testType,instances,ca,cwnd,netem,bw,pingRtt,rate,rateMin,rateMax,retransPkts%,fairness 28 | 29 | # Average rows that have the same values for the specified columns 30 | #Average=ca,testNote,req,ecn 31 | 32 | # Write Table showing selected rows and columns 33 | doTable 34 | 35 | # Plot of #Flows vs. Retrans and Fairness 36 | plotSize=1300,650 37 | source=plotFlowsRetransFairness.exp,\ 38 | subTitle1=3->1 hosts,\ 39 | subTitle2=10ms RTT and 100mbps 40 | 41 | # Plot of #Flows vs. Retrans and Fairness 42 | plotSize=1300,650 43 | source=plotFlowsRttCwnd.exp,\ 44 | subTitle1=3->1 hosts,\ 45 | subTitle2=10ms RTT and 100mbps 46 | 47 | # Select new rows to use 48 | select=all 49 | Select=exp[has].0,testType==3Stream1,netem==80,bw==100mbit 50 | 51 | # Write table showing selected rows and columns 52 | doTable 53 | 54 | # Repeat plots using new data (80ms RTT, 100mbit bw) 55 | source=plotFlowsRetransFairness.exp,\ 56 | subTitle1=3->1 hosts,\ 57 | subTitle2=80ms RTT and 100mbps 58 | 59 | source=plotFlowsRttCwnd.exp,\ 60 | subTitle1=3->1 hosts,\ 61 | subTitle2=80ms RTT and 100mbps 62 | -------------------------------------------------------------------------------- /netesto/local/exp/plotFlowsRetransFairness.exp: -------------------------------------------------------------------------------- 1 | #Y1Units=1=us,1000=ms,1000000=secs 2 | Y1Units= 3 | Y2Units= 4 | xUniform=on 5 | plotX=instances 6 | plotY1='retransPkts%' 7 | plotY2='fairness' 8 | plotSeries='ca' 9 | plotXTitle='Flows per host' 10 | plotY1Title='% Retransmits' 11 | plotY2Title='Fairness = Fastest/Slowest BW' 12 | plotSeriesTitle='CA' 13 | plotTitle='Retransmissions and Fairness|$subTitle1|$subTitle2' 14 | #plotSize=1300,650 15 | doPlot 16 | -------------------------------------------------------------------------------- /netesto/local/exp/plotFlowsRttCwnd.exp: -------------------------------------------------------------------------------- 1 | Y1Units=1=us,1000=ms,1000000=secs 2 | Y2Units= 3 | Y1min=0 4 | #Y2min=0 5 | xUniform=on 6 | #plotSize=1300,650 7 | plotXTitle='Flows per host' 8 | plotX=instances 9 | plotY1='pingRtt' 10 | plotY2='cwnd' 11 | plotSeries='ca' 12 | plotXTitle='Flows per host' 13 | plotY1Title='Ping Latency' 14 | plotY2Title='Average cwnd' 15 | plotSeriesTitle='CA' 16 | plotTitle='RTT and Cwnd|$subTitle1|$subTitle2' 17 | doPlot 18 | -------------------------------------------------------------------------------- /netesto/local/fields.txt: -------------------------------------------------------------------------------- 1 | exp:one 2 | expName:one 3 | group:one 4 | test:one 5 | testType:one 6 | testNote:one 7 | desc:one 8 | kernel:one 9 | host:one 10 | server:one 11 | instances:one 12 | instance:one 13 | ca:all 14 | dur:one 15 | delay:one 16 | req:one 17 | reply:one 18 | cwnd:avg 19 | netem:one 20 | qdisc:one 21 | bw:one 22 | buffers:one 23 | ecn:one 24 | rtt:avg 25 | pingRtt:avg 26 | rate:sumRateDur 27 | rateMin:min 28 | rateMax:max 29 | retransPkts%:avg 30 | minLatency:min 31 | maxLatency:max 32 | meanLatency:avg 33 | p50Latency:avg 34 | p90Latency:avg 35 | p99Latency:avg 36 | p999Latency:avg 37 | localRetrans:sum 38 | remoteRetrans:sum 39 | lost:sum 40 | retrans:sum 41 | retrans_total:sum 42 | localCpu:avg 43 | remoteCpu:avg 44 | tcpRmem:min 45 | tcpWmem:min 46 | tso:one 47 | gso:one 48 | lro:one 49 | gro:one 50 | adaptive_rx:avg 51 | rx-frames:avg 52 | tx-frames:avg 53 | client-rx-packets:sum 54 | client-tx-packets:sum 55 | client-rx-bytes:sum 56 | client-tx-bytes:sum 57 | client-tx-packet-len:avg 58 | client-rx-packet-len:avg 59 | -------------------------------------------------------------------------------- /netesto/local/inlib.caTest: -------------------------------------------------------------------------------- 1 | # 2 | # Basic TCP ca test 3 | # 4 | # Consists of the following tests: 5 | # A) tcpCA23 6 | # Consists of the following types lasting $dur (default 60 secs) each: 7 | # 2->1 stream test, 2nd stream starts within 1st stream 1s2cmid.vs 8 | # 3->1 stream test, 2nd & 3rd tests start within 1st 1s3cmid.vs 9 | # Tests are: 10 | # 1) 2->1 all using baselineCA - SET tcpCA23_baseline=1 11 | # 2) 3->1 all using baselineCA / 12 | # For each ca in testCA 13 | # 3) 2->1 all using ca - SET tcpCA23_test=1 14 | # 4) 3->1 all using ca / 15 | # 5) 2->1 using baselineCA for 1st flow and ca for 2nd flow - SET tcpCA23_vs1=1 16 | # 6) 3->1 using baselineCa for 1st flow and ca for 2nd and 3rd flows / 17 | # 7) 2->1 using ca for 1st flow and baselineCA for 2nd flow - SET tcpCA23_vs2=1 18 | # 8) 3->1 using ca for 1st flow and baselineCA for 2nd and 3rd flows / 19 | # 20 | #>>>> Begin comment block 21 | 22 | BEGIN_COMMENT 23 | # USAGE: Set your own script file as follows: 24 | 25 | # Set default host suffix 26 | HOST_SUFFIX mynetwork.com 27 | 28 | # Set client hosts 29 | # replace with your own hostnames 30 | SET client1=kerneltest002 31 | SET client2=kerneltest003 32 | SET client3=kerneltest004 33 | SET server1=kerneltest001 34 | 35 | SET baselineCA=cubic 36 | SET testCA=reno,bbr # CAs to test 37 | 38 | DESC ECN_9k:10K 39 | 40 | # Load library with macros 41 | SOURCE inlib.caTest 42 | 43 | SET instances=1 # how many flow instances per host 44 | SET dur=60 # duration of each run in seconds 45 | 46 | # set default reply size of RPCs 47 | SET reply=1 # use RPC reply size of 1 byte 48 | 49 | # 50 | # Define commands to run before each test 51 | # 52 | # On Server(s) 53 | # 54 | BEGIN preServer 55 | # set large receive buffers in server 56 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,20971520 57 | END preServer 58 | # 59 | # On Client(s) 60 | # 61 | BEGIN preClient 62 | # set large send buffers in client 63 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,20971520 64 | END preClient 65 | 66 | SET tcpCA23_baseline=1 67 | SET tcpCA23_test=1 68 | SET tcpCA23_vs1=1 69 | SET tcpCA23_vs2=1 70 | 71 | RUN tcpCA23 72 | 73 | END_COMMENT 74 | 75 | #>>> End Comment block 76 | 77 | SOURCE inlib 78 | 79 | SET clients1=$client1 80 | SET clients2=$client1,$client2 81 | SET clients3=$client1,$client2,$client3 82 | SET servers1=$server1 83 | 84 | SET instances=1 # how many flow instances per host 85 | SET dur=60 # duration of each run in seconds 86 | SET delay=0 87 | SET exp=COUNTER 88 | SET netemLimit=20000 89 | SET netemLoss=0 90 | 91 | # 92 | # tcpCA23 93 | # 94 | BEGIN tcpCA23 95 | 96 | # set delays and durations of flows 97 | SET_EXP dur2_2=$dur/3 98 | SET_EXP delay2_2=$dur2_2+3 99 | SET_EXP dur2_3=$dur*2/3 100 | SET_EXP delay2_3=$dur/6+3 101 | SET_EXP dur3_3=$dur/3 102 | SET_EXP delay3_3=$dur/3+5 103 | 104 | SET netemHost=$server1 105 | SET_NETEM host=$netemHost netem_delay=$delay limit=$netemLimit loss=$netemLoss 106 | 107 | # Baseline 108 | SET ca1=$baselineCA 109 | SET ca2=$baselineCA 110 | SET ca3=$baselineCA 111 | DESC $ca1:$ca2 112 | IF $tcpCA23_baseline: RUN TwoFlowBaseStream expName=1s2cmid.vs dur1=$dur dur2=$dur2_2 delay2=$delay2_2 113 | IF $tcpCA23_baseline: RUN ThreeFlowBaseStream expName=1s3cmid.vs dur1=$dur dur2=$dur2_3 dur3=$dur3_3 delay2=$delay2_3 delay3=$delay3_3 114 | 115 | FOR ca IN $testCA DO 116 | # testCA vs itself 117 | SET ca1=$ca 118 | SET ca2=$ca 119 | SET ca3=$ca 120 | DESC $ca1:$ca2 121 | IF $tcpCA23_test: RUN TwoFlowBaseStream expName=1s2cmid.vs dur1=$dur dur2=$dur2_2 delay2=$delay2_2 122 | IF $tcpCA23_test: RUN ThreeFlowBaseStream expName=1s3cmid.vs dur1=$dur dur2=$dur2_3 dur3=$dur3_3 delay2=$delay2_3 delay3=$delay3_3 123 | 124 | # testCA vs baseline 125 | SET ca1=$baselineCA 126 | SET ca2=$ca 127 | SET ca2=$ca 128 | DESC $ca1:$ca2 129 | IF $tcpCA23_vs1: RUN TwoFlowBaseStream expName=1s2cmid.vs dur1=$dur dur2=$dur2_2 delay2=$delay2_2 130 | IF $tcpCA23_vs1: RUN ThreeFlowBaseStream expName=1s3cmid.vs dur1=$dur dur2=$dur2_3 dur3=$dur3_3 delay2=$delay2_3 delay3=$delay3_3 131 | 132 | SET ca1=$ca 133 | SET ca2=$baselineCA 134 | SET ca2=$baselineCA 135 | DESC $ca1:$ca2 136 | IF $tcpCA23_vs2: RUN TwoFlowBaseStream expName=1s2cmid.vs dur1=$dur dur2=$dur2_2 delay2=$delay2_2 137 | IF $tcpCA23_vs2: RUN ThreeFlowBaseStream expName=1s3cmid.vs dur1=$dur dur2=$dur2_3 dur3=$dur3_3 delay2=$delay2_3 delay3=$delay3_3 138 | DONE 139 | 140 | SET_NETEM host=$netemHost netem_delay=0 141 | 142 | END tcpCA23 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /netesto/local/inlib.rateTest: -------------------------------------------------------------------------------- 1 | # 2 | # Basic rate tests 3 | # 4 | # Consists of the following 21 tests: 5 | # 1) 1->1: 1x1M 1s1c01.fr SET rate1r=1 \_ SET rate1=1 6 | # 2) 1->1: 1XS 1s1c01.fs SET rate1s=1 / 7 | # 3) 2->1: 1x1M 1s2c01.fr SET rate2r=1 \ 8 | # 4) 2->1: 1xS 1s2c01.fr SET rate2s=1 | 9 | # 5) 2->1: 2x1M 1s2c02.fr SET rate2r1=1 |- SET rate2=1 10 | # 6) 2->1: 2x16M 1s2c02.fr | 11 | # 7) 2->1: 2x1M,10K 1s2c02.1fr SET rate2r1=1 | 12 | # 8) 2->1: 2x16M,10K 1s2c02.1fr / 13 | # 9) 3->1: 1M 1s3c01.0fr SET rate3r1=1 \ 14 | # 10) 3->1: 2x1M 1s3c02.0fr SET rate3r2=1 | 15 | # 11) 3->1: 4x1M 1s3c04.0fr SET rate3r4=1 |- SET rate3=1 16 | # 12) 3->1: 8x1M runs twice 1s3c08.0fr | 17 | # 13) 3->1: 16x1M runs twice 1s3c16.0fr | 18 | # 14) 3->1: 32x1M runs twice 1s3c32.0fr / 19 | # ) 3->1: 1xSTREAM 1s3c01fs SET rate3s1=1 \ 20 | # ) 3->1: 2xSTREAM 1s3c02fs SET rate3s2=1 | 21 | # 15) 3->1: 4xSTREAM 1s3c04fs SET rate3s4=1 | 22 | # 16) 3->1: 8xSTREAM 1s3c08fs |- SET rate3s=1 23 | # 17) 3->1:16xSTREAM 1s3c16fs / 24 | # 18) 3->1: 1x 10K,1M 1s3c01.pfr SET rate3p1=1 \ 25 | # 19) 3->1: 2x 10K,1M 1s3c02.pfr SET rate3p2=1 | 26 | # 20) 3->1: 4x 10K,1M 1s3c04.pfr SET rate3p4=1 |- SET rate3p=1 27 | # 21) 3->1: 8x 10K,1M 1s3c08.pfr SET rate3p8=1 | 28 | # 22) 3->1: 16x 10K,1M 1s3c16.pfr SET rate3p16=1 | 29 | # 23) 3->1: 32x 10K,1M 1s3c32.pfr SET rate3p32=1 / 30 | # 24) 3->1: 1x 8M,1M,50K,10K 1s3c01.xfr \ 31 | # 25) 3->1: 2x 8M,1M,50K,10K 1s3c02.xfr | 32 | # 26) 3->1: 4x 8M,1M,50K,10K 1s3c04.xfr | 33 | # 27) 3->1: 8x 8M,1M,50K,10K 1s3c08.xfr |- SET rate3x=1 34 | # 28) 3->1: 16x 8M,1M,50K,10K 1s3c16.xfr | 35 | # 29) 3->1: 32x 8M,1M,50K,10K 1s3c32.xfr / 36 | # 37 | 38 | ## USAGE: 39 | ## Set default host suffix 40 | #HOST_SUFFIX mynetwork.com 41 | 42 | ## Set hosts for 1, 2, 3 or 4 client host experiments 43 | ## replace with your own hostnames 44 | #SET client1=kerneltest010 45 | #SET client2=kerneltest011 46 | #SET client3=kerneltest012 47 | #SET servers1=kerneltest014 48 | #SET ca=reno,cubic 49 | #DESC ECN_9k:10K 50 | 51 | #SET clients1=$client1 52 | #SET clients2=$client1,$client2 53 | #SET clients3=$client1,$client2,$client3 54 | 55 | ## Load library with macros 56 | #SOURCE inlib 57 | #SOURCE inlib.rateTest 58 | 59 | #SET instances=1 # how many flow instances per host 60 | #SET dur=60 # duration of each run in seconds 61 | 62 | 63 | ## set default reply size of RPCs 64 | #SET reply=1 # use RPC reply size of 1 byte 65 | 66 | # 67 | # Define commands to run before each test 68 | # 69 | # On Server(s) 70 | # 71 | #BEGIN preServer 72 | ## set large receive buffers in server 73 | #SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,20971520 74 | #END preServer 75 | # 76 | # On Client(s) 77 | # 78 | #BEGIN preClient 79 | ## set large send buffers in client 80 | #SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,20971520 81 | #END preClient 82 | 83 | # RUN ExpRate 84 | 85 | BEGIN ExpRate 86 | 87 | # 88 | # 1->1. 1 flow RR and STREAM. SET rate1=1 89 | # 90 | # 1 server, 1 client, 1M 91 | SET reqs=1M 92 | IF $reqs1F: SET reqs=$reqs1 93 | IF $rate1: RUN MServerRR servers=$servers1 clients=$clients1 expName=1s1c1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 94 | 95 | IF $rate1r: RUN MServerRR servers=$servers1 clients=$clients1 expName=1s1c1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 96 | 97 | # 1 server, 1 client Stream 98 | IF $rate1: RUN MServerStream servers=$servers1 clients=$clients1 expName=1s1c1fs ca=$ca dur=$dur delay=0 instances=$instances 99 | 100 | IF $rate1s: RUN MServerStream servers=$servers1 clients=$clients1 expName=1s1c1fs ca=$ca dur=$dur delay=0 instances=$instances 101 | 102 | # 103 | # 2->1, 10K,1X1M. SET rate2=1 104 | # 105 | # 1 servers, 2 clients, 1M RPCs (1 flows per client) 106 | SET reqs=1M 107 | IF $rate2: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c1.fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 108 | IF $rate2r: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c1.fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 109 | 110 | # 1 servers, 2 clients, 1 STREAM (1 flows per client) 111 | IF $rate2: RUN MServerStream servers=$servers1 clients=$clients2 expName=1s2c1.fs ca=$ca dur=$dur delay=0 instances=$instances 112 | IF $rate2s: RUN MServerStream servers=$servers1 clients=$clients2 expName=1s2c1.fs ca=$ca dur=$dur delay=0 instances=$instances 113 | 114 | # 1 servers, 2 clients, 2x1M RPCs (2 flows per client) 115 | SET reqs=1M,1M 116 | IF $rate2: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 117 | IF $rate2r1: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 118 | 119 | # 1 servers, 2 clients, 2x16M RPCs (2 flows per client) 120 | SET reqs=16M,16M 121 | IF $rate2: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 122 | 123 | # 1 servers, 2 clients, 2x1M and 10K RPCs (3 flows per client) 124 | SET reqs=1M,1M,10K 125 | IF $rate2: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 126 | IF $rate2r1: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 127 | 128 | # 1 servers, 2 clients, 2x16M and 10K RPCs (3 flows per client) 129 | SET reqs=16M,16M,10K 130 | IF $rate2: RUN MServerRR servers=$servers1 clients=$clients2 expName=1s2c2.1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 131 | 132 | # 133 | # 3->1. 1, 2, 4, 8, 16, 32 flows. SET rate3=1 134 | # 135 | # 1 server, 3 client 1M 136 | IF $rate3: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c01.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=1M reply=1 137 | IF $rate3r1: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c01.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=1M reply=1 138 | 139 | # 1 server, 3 client, 2x1M 140 | SET reqs=1M,1M 141 | IF $rate3: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c02.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=$reqs reply=1 142 | IF $rate3r2: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c02.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=$reqs reply=1 143 | 144 | # 1 server, 3 client, 4x1M 145 | SET reqs=1M,1M,1M,1M 146 | IF $rate3: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=$reqs reply=1 147 | SET reqs=1M,1M,1M,1M 148 | IF $rate3r4: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.0fr ca=$ca dur=$dur delay=0 instances=1 reqs=$reqs reply=1 149 | 150 | # 1 server, 3 client, 8x1M 151 | SET reqs=1M,1M,1M,1M 152 | IF $rate3: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c08.0fr ca=$ca dur=$dur delay=0 instances=2 reqs=$reqs reply=1 153 | 154 | # 1 server, 3 client, 16x1M 155 | SET reqs=1M,1M,1M,1M 156 | IF $rate3: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c16.0fr ca=$ca dur=$dur delay=0 instances=4 reqs=$reqs reply=1 157 | 158 | # 1 server, 3 client, 32x1M 159 | SET reqs=1M,1M,1M,1M 160 | IF $rate3: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c32.0fr ca=$ca dur=$dur delay=0 instances=8 reqs=$reqs reply=1 161 | 162 | # 163 | # 3->1. STREAM: 1, 4, 8, 16, 32 flows. SET $rate3s=1 164 | # 165 | # 1 server, 3 client, 1xSTREAM 166 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c01fs ca=$ca dur=$dur delay=0 instances=1 167 | IF $rate3s1: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c01fs ca=$ca dur=$dur delay=0 instances=1 168 | 169 | # 1 server, 3 client, 2xSTREAM 170 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c02fs ca=$ca dur=$dur delay=0 instances=2 171 | IF $rate3s2: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c02fs ca=$ca dur=$dur delay=0 instances=2 172 | 173 | # 1 server, 3 client, 4xSTREAM 174 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c04fs ca=$ca dur=$dur delay=0 instances=4 175 | IF $rate3s4: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c04fs ca=$ca dur=$dur delay=0 instances=4 176 | 177 | # 1 server, 3 client, 8xSTREAM 178 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c08fs ca=$ca dur=$dur delay=0 instances=8 179 | 180 | # 1 server, 3 client, 8xSTREAM for testing 181 | IF $rate3s8: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c08fs ca=$ca dur=$dur delay=0 instances=8 182 | 183 | # 1 server, 3 client, 16xSTREAM 184 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c16fs ca=$ca dur=$dur delay=0 instances=16 185 | 186 | # 1 server, 3 client, 32xSTREAM 187 | IF $rate3s: RUN MServerStream servers=$servers1 clients=$clients3 expName=1s3c32fs ca=$ca dur=$dur delay=0 instances=32 188 | 189 | # 190 | # 3->1. 1M,10K: 1, 2, 4, 8 , 16, 32 flows. SET rate3p=1 191 | # 192 | # 1 server, 3 client 1M,10K 193 | IF $rate3p: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c01.pfr ca=$ca dur=$dur delay=0 instances=1 reqs=1M,10K reply=1 194 | 195 | IF $rate3p1: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c01.pfr ca=$ca dur=$dur delay=0 instances=1 reqs=1M,10K reply=1 196 | 197 | # 1 server, 3 client, 2x 1M,10K 198 | SET servers2x=$servers1,$servers1 199 | IF $rate3p: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c02.pfr ca=$ca dur=$dur delay=0 instances=2 reqs=1M,10K reply=1 200 | 201 | IF $rate3p2: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c02.pfr ca=$ca dur=$dur delay=0 instances=1 reqs=1M,10K reply=1 202 | 203 | # 1 server, 3 client, 4x 1M,10K 204 | SET servers4x=$servers1,$servers1,$servers1,$servers1 205 | IF $rate3p: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.pfr ca=$ca dur=$dur delay=0 instances=4 reqs=1M,10K reply=1 206 | 207 | IF $rate3p4: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.pfr ca=$ca dur=$dur delay=0 instances=4 reqs=1M,10K reply=1 208 | 209 | # 1 server, 3 client, 8x 1M,10K 210 | IF $rate3p: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c08.pfr ca=$ca dur=$dur delay=0 instances=8 reqs=1M,10K reply=1 211 | 212 | IF $rate3p8: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.pfr ca=$ca dur=$dur delay=0 instances=8 reqs=1M,10K reply=1 213 | 214 | # 1 server, 3 client, 16x 1M,10K 215 | IF $rate3p: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c16.pfr ca=$ca dur=$dur delay=0 instances=16 reqs=1M,10K reply=1 216 | 217 | IF $rate3p16: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c16.pfr ca=$ca dur=$dur delay=0 instances=16 reqs=1M,10K reply=1 218 | 219 | # 1 server, 3 client, 32x 1M,10K 220 | IF $rate3p: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c32.pfr ca=$ca dur=$dur delay=0 instances=32 reqs=1M,10K reply=1 221 | 222 | IF $rate3p32: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c32.pfr ca=$ca dur=$dur delay=0 instances=32 reqs=1M,10K reply=1 223 | 224 | # 225 | # 3->1. 8M,1M,50K,10K: 1, 2, 4, 8 , 16, 32 flows. SET rate3x=1 226 | # 227 | # 1 server, 3 client 8M,1M,50K,10K 228 | IF $rate3x: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c01.xfr ca=$ca dur=$dur delay=0 instances=1 reqs=8M,1M,50K,10K reply=1 229 | 230 | # 1 server, 3 client, 2x 8M,1M,50K,10K 231 | IF $rate3x: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c02.xfr ca=$ca dur=$dur delay=0 instances=2 reqs=8M,1M,50K,10K reply=1 232 | 233 | # 1 server, 3 client, 4x 8M,1M,50K,10K 234 | IF $rate3x: RUN MServerRR servers=$servers1 clients=$clients3 expName=1s3c04.xfr ca=$ca dur=$dur delay=0 instances=4 reqs=8M,1M,50K,10K reply=1 235 | 236 | # 1 server, 3 client, 8x 8M,1M,50K,10K 237 | IF $rate3x: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c08.xfr ca=$ca dur=$dur delay=0 instances=8 reqs=8M,1M,50K,10K reply=1 238 | 239 | # 1 server, 3 client, 16x 8M,1M,50K,10K 240 | IF $rate3x: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c16.xfr ca=$ca dur=$dur delay=0 instances=16 reqs=8M,1M,50K,10K reply=1 241 | 242 | # 1 server, 3 client, 32x 8M,1M,50K,10K 243 | IF $rate3x: RUN MServerRR,2 servers=$servers1 clients=$clients3 expName=1s3c32.xfr ca=$ca dur=$dur delay=0 instances=32 reqs=8M,1M,50K,10K reply=1 244 | 245 | END ExpRate 246 | 247 | 248 | -------------------------------------------------------------------------------- /netesto/local/inlib.vsTest: -------------------------------------------------------------------------------- 1 | # 2 | # Versus Tests 2 -> 1 & 3 -> 1 3 | # Examine the interaction of 2 TCP variants 4 | # a) ca1, ca2: ca1 for client 1, ca2 for clients 2 & 3 5 | # b) use netem to specify client 1's RTT 6 | # 7 | # Set default host suffix 8 | #HOST_SUFFIX mynetwork.com 9 | 10 | # Set hosts for each client 11 | # replace with your own hostnames 12 | #SET client1=kerneltest010 13 | #SET client2=kerneltest013 14 | 15 | #SET clients1=$client1 16 | #SET clients2=$client1,$client2 17 | 18 | #SET servers1=kerneltest014 19 | 20 | # Set congestion control variants to run 21 | #SET ca1=dctcp 22 | #SET ca2=dctcp 23 | 24 | #SET instances=1 # how many flow instances per host 25 | #SET dur=60 # duration of each run in seconds 26 | 27 | # specify descripiton of experiments 28 | #DESC ECN_400:1802+RTT+VS_RTT_dctcp:dctcp 29 | 30 | # set default reply size of RPCs 31 | #SET reply=1 # use RPC reply size of 1 byte 32 | 33 | # 34 | # Define Macro for VS experiments 35 | # 36 | BEGIN ExpVs 37 | 38 | # --- 2 -> 1 39 | SET clients2=$client2 40 | 41 | # 1 servers, 2 clients, 1M and 10K RPCs (2 flows per client, 1 for each server) 42 | SET reqs=1M,10K 43 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c2frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 44 | 45 | IF_DEF $ExpVsOne: END 46 | 47 | # 1 servers, 2 clients, 8M and 10K RPCs (2 flows per client, 1 for each server) 48 | SET reqs=8M,10K 49 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c2frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 50 | 51 | # 1 servers, 2 clients, Stream 52 | IF 1: RUN MServerStreamVs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c2fsvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances 53 | 54 | # 1 servers, 2 clients, 2x1M and 1x10K RPCs 55 | SET reqs=1M,1M,10K 56 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c3frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 57 | 58 | # 1 servers, 2 clients, 4x1M and 1x10K RPCs 59 | SET reqs=1M,1M,1M,1M,10K 60 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c5frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 61 | 62 | # 1 servers, 2 clients, 8x1M and 1x10K RPCs 63 | SET reqs=1M,1M,1M,1M,1M,1M,1M,1M,10K 64 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c9frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 65 | 66 | # 1 servers, 2 clients, 16x1M and 1x10K RPCs 67 | SET reqs=1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,10K 68 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c17frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 69 | 70 | # 1 servers, 2 clients, 8x8M and 1x10K RPCs 71 | SET reqs=8M,8M,8M,8M,8M,8M,8M,8M,10K 72 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s2c9fRvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 73 | 74 | # --- 3 -> 1 75 | SET clients2=$client2,$client3 76 | # 1 servers, 3 clients, 1M and 10K RPCs (2 flows per client, 1 for each server) 77 | SET reqs=1M,10K 78 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c2frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 79 | 80 | # 1 servers, 2 clients, 8M and 10K RPCs (2 flows per client, 1 for each server) 81 | SET reqs=8M,10K 82 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c2frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 83 | 84 | # 1 servers, 2 clients, Stream 85 | IF 1: RUN MServerStreamVs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c2fsvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances 86 | 87 | # 1 servers, 2 clients, 2x1M and 1x10K RPCs 88 | SET reqs=1M,1M,10K 89 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c3frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 90 | 91 | # 1 servers, 2 clients, 4x1M and 1x10K RPCs 92 | SET reqs=1M,1M,1M,1M,10K 93 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c5frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 94 | 95 | # 1 servers, 2 clients, 8x1M and 1x10K RPCs 96 | SET reqs=1M,1M,1M,1M,1M,1M,1M,1M,10K 97 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c9frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 98 | 99 | # 1 servers, 2 clients, 16x1M and 1x10K RPCs 100 | SET reqs=1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,1M,10K 101 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c17frvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 102 | 103 | # 1 servers, 2 clients, 8x8M and 1x10K RPCs 104 | SET reqs=8M,8M,8M,8M,8M,8M,8M,8M,10K 105 | IF 1: RUN MServerRRvs servers=$servers1 clients1=$client1 clients2=$clients2 expName=1s3c9fRvs ca1=$ca1 ca2=$ca2 dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 106 | 107 | END ExpVs 108 | 109 | 110 | # Set congestion control variants to run 111 | #SET ca1=cubic 112 | #SET ca2=cubic 113 | #DESC ECN_9k:10k+RTT+VS_RTT_cubic:cubic 114 | #RUN ExpVs ca1=cubic ca2=cubic 115 | 116 | #NEXT_COUNTER 117 | 118 | #SET ca1=reno 119 | #SET ca2=reno 120 | #DESC ECN_9k:10k+RTT+VS_RTT_reno:reno 121 | #RUN ExpVs ca1=reno ca2=reno 122 | 123 | -------------------------------------------------------------------------------- /netesto/local/makeResultsPage.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | import sys 4 | import os 5 | 6 | htmlHeader = [ 7 | '', 8 | '', 9 | ' Exp', 10 | ' ', 25 | '', 26 | '' 27 | ] 28 | 29 | fieldList = [ 30 | 'group', 'Test', 'host', 'server', 'instances', 'dur', 'delay', 31 | 'Ca', 'min/avg/max Rates', 32 | 'min/mean/max Latencies', 'p50/p90/p99 Latencies', 33 | 'rtt', 'pingRtt', 'cwnd', 34 | 'localRetrans', 'remoteRetrans', 'lost', 'retrans', 'retrans_total', 35 | 'localCpu', 'remoteCpu', 36 | 'client-tx-packets', 'client-tx-bytes', 'client-tx-packet-len', 37 | 'client-rx-packets', 'client-rx-bytes', 'client-rx-packet-len', 38 | 'tso', 'gso', 'lro', 'gro', 'rx-frames', 'tx-frames', 'adaptive-rx' 39 | ] 40 | 41 | machinePath = None 42 | nvFlag = False 43 | 44 | def processFile(f): 45 | global machinePath 46 | global nvFlag 47 | 48 | #print 'processing:', f 49 | fieldDict = {} 50 | fin = open(f, 'r') 51 | for line in fin: 52 | line = line.strip() 53 | kv = line.split(':') 54 | if len(kv) < 2: 55 | continue 56 | key, val = kv 57 | fieldDict[key] = val 58 | if key == 'ca' and val == 'nv': 59 | nvFlag = True 60 | if key == 'host' or key == 'server': 61 | i = val.find('.') 62 | if i >= 0: 63 | mp = val[i:] 64 | if machinePath == None: 65 | machinePath = mp 66 | elif machinePath != mp: 67 | machinePath = '' 68 | fin.close() 69 | #print ' len:', len(fieldDict) 70 | return fieldDict 71 | 72 | def processFields(fieldDict): 73 | if 'test' in fieldDict: 74 | test = fieldDict['test'] 75 | if 'req' in fieldDict and 'reply' in fieldDict: 76 | test += ' ' + fieldDict['req'] + '/' + fieldDict['reply'] 77 | fieldDict['Test'] = test 78 | if 'ca' in fieldDict: 79 | ca = fieldDict['ca'] 80 | if 'nvPad' in fieldDict: 81 | ca += '/' + fieldDict['nvPad'] 82 | fieldDict['Ca'] = ca 83 | if 'rate' in fieldDict: 84 | rates = fieldDict['rate'] 85 | if 'rateMin' in fieldDict and 'rateMax' in fieldDict: 86 | rates = fieldDict['rateMin'] + '/' + rates + '/' + \ 87 | fieldDict['rateMax'] 88 | else: 89 | rates = '?/' + rates + '/?' 90 | fieldDict['min/avg/max Rates'] = rates 91 | if 'meanLatency' in fieldDict: 92 | lats = fieldDict['meanLatency'] 93 | if 'minLatency' in fieldDict and 'maxLatency' in fieldDict: 94 | lats = fieldDict['minLatency'] + '/' + lats + '/' + \ 95 | fieldDict['maxLatency'] 96 | else: 97 | lats = '?/' + lats + '/?' 98 | fieldDict['min/mean/max Latencies'] = lats 99 | if 'p50Latency' in fieldDict: 100 | plats = fieldDict['p50Latency'] 101 | if 'p90Latency' in fieldDict and 'p99Latency' in fieldDict: 102 | plats = plats + '/' + fieldDict['p90Latency'] + '/' + \ 103 | fieldDict['p99Latency'] 104 | else: 105 | plats = plats + '/?/?' 106 | fieldDict['p50/p90/p99 Latencies'] = plats 107 | return fieldDict 108 | 109 | 110 | def writeHtmlHeader(exp): 111 | fout = open(exp + '/' + 'exp.html', 'w') 112 | for line in htmlHeader: 113 | fout.write('%s\n' % line) 114 | return fout 115 | 116 | exp = sys.argv[1] 117 | files = os.listdir(exp) 118 | fieldDictList = [] 119 | 120 | for f in files: 121 | if f.find('.exp.out') >= 0 and f.find('all') < 0: 122 | fieldDict = processFile(exp + '/' + f) 123 | if len(fieldDict) > 0: 124 | fieldDict = processFields(fieldDict) 125 | fieldDictList.append(fieldDict) 126 | 127 | if len(fieldDictList) == 0: 128 | #print 'len(fieldDictList) == 0!!' 129 | sys.exit(0) 130 | 131 | fout = writeHtmlHeader(exp) 132 | nextExp = str(int(exp) + 1) 133 | prevExp = str(int(exp) - 1) 134 | fout.write(('

Exp:%s %s         ' + 135 | 'Prev       ' + 136 | 'Up       ' + 137 | 'Next

\n') % 138 | (exp, fieldDictList[0]['expName'], prevExp, nextExp)) 139 | fout.write(' \n') 140 | 141 | if machinePath != '' and machinePath != None: 142 | for fd in fieldDictList: 143 | if 'client' in fd: 144 | v = fd['client'] 145 | i = v.find('.') 146 | fd['client'] = v[:i] 147 | if 'server' in fd: 148 | v = fd['server'] 149 | i = v.find('.') 150 | fd['server'] = v[:i] 151 | 152 | for field in fieldList: 153 | if field == 'group': 154 | tx = 'th' 155 | else: 156 | tx = 'td' 157 | fout.write(' \n <%s>%s\n' % (tx, field, tx)) 158 | for fd in fieldDictList: 159 | if field in fd: 160 | v = fd[field] 161 | else: 162 | v = ' ' 163 | fout.write(' <%s>%s\n' % (tx, v, tx)) 164 | fout.write(' \n') 165 | 166 | fout.write('
\n') 167 | fout.write(' \n') 168 | fout.write(' \n') 169 | #fout.write(' \n') 170 | fout.write(' \n') 171 | fout.write(' \n') 172 | fout.write(' \n') 173 | fout.write(' \n') 174 | fout.write(' \n') 175 | fout.write('\n\n') 176 | fout.close() 177 | 178 | expName = fieldDictList[0]['expName'] 179 | 180 | fout = open('ExpList.html', 'a') 181 | fout.write('

%s %s

\n' % (exp, exp, expName)) 182 | fout.close() 183 | -------------------------------------------------------------------------------- /netesto/local/plotMonitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | 3 | import sys 4 | import os 5 | import psPlot 6 | 7 | def processFileRetrans(inFilename, field): 8 | inFile = open(inFilename, 'r') 9 | X = [] 10 | delay = 0.0 11 | firstTime = True 12 | time = -0.200 13 | lastY = -340987 14 | for line in inFile: 15 | line = line.strip() 16 | if line.find("Recv-Q") >= 0: 17 | time += 0.200 18 | kvals = line.split(' ') 19 | for kv in kvals: 20 | if kv.find(':') < 0: 21 | continue 22 | key, val = kv.split(':', 1) 23 | if val.find('/') > 0: 24 | val = val[val.find('/') + 1:] 25 | if firstTime: 26 | if key == 'delay': 27 | delay = float(val) 28 | firstTime = False 29 | break 30 | if key == field: 31 | y = float(val) 32 | if y > lastY: 33 | X.append(time+delay) 34 | lastY = y 35 | inFile.close() 36 | return X 37 | 38 | def processFile(inFilename, field): 39 | caNames = ['cubic', 'reno', 'nv', 'dctcp', 'bbr', 'bic', 'cdg', 40 | 'highspeed', 'htcp', 'hybla', 'illinois', 'lp', 'westwood', 41 | 'yeah'] 42 | 43 | inFile = open(inFilename, 'r') 44 | X = [] 45 | Y = [] 46 | ca = "?" 47 | delay = 0.0 48 | time = -0.200 49 | firstTime = True 50 | old_acked = 0 51 | new_acked = 0 52 | 53 | for line in inFile: 54 | line = line.strip() 55 | # print 'Line: ', line 56 | if line.find("Recv-Q") >= 0: 57 | time += 0.200 58 | 59 | if firstTime: 60 | for caName in caNames: 61 | if line.find(' ' + caName + ' ') > 0: 62 | ca = caName 63 | if ca != '?': 64 | firstTime = False 65 | 66 | kvals = line.split(' ') 67 | 68 | for kv in kvals: 69 | if kv.find(':') < 0: 70 | continue 71 | key, val = kv.split(':', 1) 72 | if key == 'delay': 73 | delay = float(val) 74 | if delay > 0: 75 | X.append(0.0) 76 | X.append(delay) 77 | Y.append(0.0) 78 | Y.append(0.0) 79 | if key == field: 80 | if val.find('/') > 0: 81 | if field == 'rtt': 82 | val = val.split('/')[0] 83 | else: 84 | val = val.split('/')[1] 85 | if field == 'send': 86 | p = val.find('bps') 87 | if p > 0: 88 | m = val[p-1] 89 | val = float(val[:p-1]) 90 | if m == 'K': 91 | val /= 1000.0 92 | elif m == 'G': 93 | val *= 1000.0 94 | elif field == 'bytes_acked': 95 | new_acked = int(val) - old_acked 96 | old_acked = int(val) 97 | val = new_acked*8/(0.200 * 1000000) 98 | Y.append(float(val)) 99 | X.append(time + delay) 100 | # elif key == 'time': 101 | # X.append(float(val) + delay) 102 | inFile.close() 103 | return X, Y, ca 104 | 105 | 106 | if len(sys.argv) < 3: 107 | #print "Usage: %s " % sys.argv[0] 108 | sys.exit(1) 109 | 110 | field = sys.argv[1] 111 | #print 'Field:', field 112 | 113 | XList = [] 114 | YList = [] 115 | caList = [] 116 | flowList = [] 117 | flowNames = [None] 118 | Xdrop = [] 119 | 120 | firstTime = True 121 | 122 | for name in sys.argv[2:]: 123 | flow = os.path.basename(name) 124 | flow = flow.replace('monitor.', '') 125 | flow = flow.replace('.out', '') 126 | #print 'processing:', name, ' flow:', flow 127 | X, Y, ca = processFile(name, field) 128 | Xd = processFileRetrans(name, 'retrans') 129 | if len(Xd) > 0: 130 | Xdrop.extend(Xd) 131 | if len(X) != len(Y) or len(X) == 0: 132 | continue 133 | 134 | if firstTime: 135 | firstTime = False 136 | xmin = X[0] 137 | xmax = X[0] 138 | ymin = Y[0] 139 | ymax = Y[0] 140 | 141 | XList.append(X) 142 | YList.append(Y) 143 | caList.append(ca) 144 | flowList.append(flow) 145 | flowNames.append(ca + ' ' + flow) 146 | xmin = min(xmin, min(X)) 147 | xmax = max(xmax, max(X)) 148 | ymin = min(ymin, min(Y)) 149 | ymax = max(ymax, max(Y)) 150 | 151 | if len(XList) == 0: 152 | sys.exit(0) 153 | 154 | #print 'xmin:%.2f, xmax:%.2f, ymin:%.2f, ymax:%.2f' % (xmin, xmax, ymin, ymax) 155 | 156 | for X in XList: 157 | i = 0 158 | for x in X: 159 | X[i] = x - xmin 160 | i += 1 161 | 162 | path = os.path.dirname(sys.argv[2]) 163 | 164 | if field == 'bytes_acked': 165 | field = 'acked_rate' 166 | p = psPlot.PsPlot(path + '/' + field, '', '', 1) 167 | p.SetPlotBgLevel(0.95) 168 | if field == 'rtt': 169 | yTitle = 'RTT (ms)' 170 | elif field == 'acked_rate': 171 | yTitle = 'Acked Rate (Mbps)' 172 | else: 173 | yTitle = field 174 | 175 | p.SetPlot(0, xmax-xmin, 0, 0, ymax, 0, 'Time in seconds', yTitle, 176 | field) 177 | p.seriesTitle = 'Flows' 178 | p.SeriesNames(flowNames) 179 | 180 | #p.PlotVBars(Xdrop, '{ 0.7 0.6 0.6 setrgbcolor } plotVBarsC') 181 | #p.PlotVBars(Xdrop, '{ 0.9 0.8 0.8 setrgbcolor } plotVBarsC') 182 | p.PlotVBars(Xdrop, '{ 1.0 0.4 0.4 setrgbcolor } plotVBarsC') 183 | 184 | i = 0 185 | for X in XList: 186 | Y = YList[i] 187 | inc = X[1] - X[0] 188 | if X[0] > 0: 189 | Xt = [] 190 | Yt = [] 191 | Xt.append(0.0) 192 | Xt.append(X[0] - inc) 193 | Xt.extend(X) 194 | X = Xt 195 | Yt.append(0.0) 196 | Yt.append(0.0) 197 | Yt.extend(Y) 198 | Y = Yt 199 | if X[-1] < xmax: 200 | X.append(X[-1] + 0.1) 201 | X.append(xmax) 202 | Y.append(0.0) 203 | Y.append(0.0) 204 | p.PlotData(1, X, Y, 205 | '', '', '0.4 ' + p.SetColor(p.colors[(i + 1) % p.colorsN]) + 206 | ' plotStraightLinesC') 207 | i += 1 208 | 209 | image = p.GetImage() 210 | #print 'Plot: ', image 211 | 212 | -------------------------------------------------------------------------------- /netesto/local/plotNetperfRates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys 4 | import os 5 | import psPlot 6 | 7 | def processFile(inFilename): 8 | inFile = open(inFilename, 'r') 9 | reqSize = 0 10 | replySize = 0 11 | units = None 12 | X = [] 13 | Y = [] 14 | ca = "?" 15 | flow = "?" 16 | lastX = 0 17 | 18 | for line in inFile: 19 | line = line.strip() 20 | # print 'Line: ', line 21 | kv = line.split('=') 22 | # print ' kv: ', kv 23 | if units == None and kv[0].find('NETPERF_UNITS') >= 0: 24 | units = kv[1] 25 | elif kv[0].find('NETPERF_INTERVAL') >= 0: 26 | i = float(kv[1]) 27 | elif kv[0].find('NETPERF_ENDING') >= 0: 28 | x = float(kv[1]) 29 | if (x - i) > lastX: 30 | X.append(x - i) 31 | else: 32 | X.append(lastX + 0.01) 33 | X.append(x) 34 | lastX = x 35 | elif kv[0].find('NETPERF_INTERIM_RESULT') >= 0: 36 | y = float(kv[1]) 37 | Y.append(y) 38 | Y.append(y) 39 | elif kv[0] == 'REQUEST_SIZE': 40 | reqSize = int(kv[1]) 41 | elif kv[0] == 'RESPONSE_SIZE': 42 | replySize = int(kv[1]) 43 | elif kv[0] == 'CA': 44 | ca = kv[1] 45 | elif kv[0] == 'FLOW': 46 | flow = kv[1] 47 | 48 | if units == 'Trans/s': 49 | i = 0 50 | if reqSize > replySize: 51 | size = reqSize 52 | else: 53 | size = replySize 54 | for y in Y: 55 | Y[i] = reqSize * 8 * y / 1000000 56 | i += 1 57 | 58 | return X, Y, ca, flow 59 | 60 | def addGraphs(XList, YList, xmin=None, xmax=None): 61 | 62 | if len(XList) <= 1: 63 | return None, None 64 | 65 | # Find minimum and maximum of X lists 66 | if xmin == None or xmax == None: 67 | xmin = XList[0][0] 68 | xmax = XList[0][0] 69 | for X in XList: 70 | xmin = min(xmin, min(X)) 71 | xmax = max(xmax, max(X)) 72 | 73 | # Find average xinc 74 | xincSum = 0.0 75 | xincCount = 0 76 | for X in XList: 77 | i = 0 78 | for x in X: 79 | if i > 0: 80 | xincSum += X[i] - X[i-1] 81 | xincCount += 1 82 | i += 1 83 | if xincCount == 0: 84 | return None, None 85 | xinc = xincSum / xincCount 86 | if xinc == 0: 87 | return None, None 88 | x = xmin 89 | n = int((xmax - xmin)/xinc + 1) 90 | #print 'xmin:%.2f xmax:%.2f xinc:%.2f' % (xmin, xmax, xinc) 91 | 92 | listIndex = -1 93 | firstTime = True 94 | for X in XList: 95 | listIndex += 1 96 | Y = YList[listIndex] 97 | if firstTime: 98 | firstTime = False 99 | Xsum = X 100 | Ysum = Y 101 | else: 102 | newXsum = [] 103 | newYsum = [] 104 | index = 0 105 | sumIndex = 0 106 | xsum = Xsum[0] 107 | x = X[0] 108 | if x > xsum: 109 | sumIndex = 0 110 | ysum = Ysum[sumIndex] 111 | index = -1 112 | y = 0 113 | elif x < xsum: 114 | sumIndex = -1 115 | ysum = 0 116 | index = 0 117 | y = Y[index] 118 | else: 119 | sumIndex = 0 120 | index = 0 121 | ysum = Ysum[sumIndex] 122 | y = Y[index] 123 | while True: 124 | if x > xsum: 125 | newXsum.append(xsum) 126 | newYsum.append(ysum + y) 127 | sumIndex += 1 128 | elif x < xsum: 129 | newXsum.append(x) 130 | newYsum.append(ysum + y) 131 | index += 1 132 | else: 133 | newXsum.append(x) 134 | newYsum.append(ysum + y) 135 | index += 1 136 | sumIndex += 1 137 | if index < len(X): 138 | if index >= 0: 139 | x = X[index] 140 | y = Y[index] 141 | else: 142 | while sumIndex < len(Xsum): 143 | xsum = Xsum[sumIndex] 144 | ysum = Ysum[sumIndex] 145 | newXsum.append(xsum) 146 | newYsum.append(ysum) 147 | sumIndex += 1 148 | break 149 | if sumIndex < len(Xsum): 150 | if sumIndex >= 0: 151 | xsum = Xsum[sumIndex] 152 | ysum = Ysum[sumIndex] 153 | else: 154 | while index < len(X): 155 | x = X[index] 156 | y = Y[index] 157 | newXsum.append(x) 158 | newYsum.append(y) 159 | index += 1 160 | break 161 | Xsum = newXsum 162 | Ysum = newYsum 163 | 164 | # for xnew in X: 165 | # ynew = Y[sumIndex] 166 | # if xnew > xold: 167 | # newXsum[newSumIndex] = x 168 | # 169 | # Xsum = XList[0] 170 | # Ysum = YList[0] 171 | # 172 | # 173 | # # fill Xsum list 174 | # x = xmin 175 | # i = 0 176 | # while x < xmax and i < n: 177 | # x = xmin + i * xinc 178 | # Xsum[i] = x 179 | # i += 1 180 | # 181 | # # fill YSum list 182 | # Ysum = [0.0]*n 183 | # listIndex = 0 184 | # for X in XList: 185 | # Y = YList[listIndex] 186 | # #print 'listIndex:', listIndex 187 | # listIndex += 1 188 | # i = 0 189 | # isum = 0 190 | # x0 = xmin - xinc 191 | # y0 = 0 192 | # x1 = X[i] 193 | # y1 = Y[i] 194 | # for x in Xsum: 195 | # while x > x1 and i < len(X) - 1: 196 | # x0 = x1 197 | ## y0 = y1 198 | # i += 1 199 | # x1 = X[i] 200 | # y1 = Y[i] 201 | ## if i >= len(X) - 1: 202 | # if i > len(X) - 1: 203 | # continue 204 | # if i == 0: 205 | # y = 0.0 206 | # else: 207 | # y = y0 + (y1 - y0)*(x - x0)/(x1 - x0) 208 | ## print 'x:%.2f y:%.2f x0:%.2f x1:%.2f y0:%.2f y1:%.2f' \ 209 | ## % (x, y, x0, x1, y0, y1) 210 | # Ysum[isum] += y 211 | # if Ysum[isum] > 10*1024.0: 212 | # Ysum[isum] = 10*1024.0 213 | # isum += 1 214 | return Xsum, Ysum 215 | 216 | # 217 | # MAIN 218 | # 219 | if len(sys.argv) < 2: 220 | print "Usage: %s " % sys.argv[0] 221 | sys.exit(1) 222 | 223 | XList = [] 224 | YList = [] 225 | caList = [] 226 | flowList = [] 227 | flowNames = [] 228 | 229 | xmax = None 230 | xmin = None 231 | 232 | firstTime = True 233 | 234 | for name in sys.argv[1:]: 235 | X, Y, ca , flow = processFile(name) 236 | if firstTime: 237 | if len(X) > 0 and len(Y) > 0: 238 | firstTime = False 239 | xmin = X[0] 240 | xmax = X[0] 241 | ymin = Y[0] 242 | ymax = Y[0] 243 | if len(X) == 0 or len(Y) == 0: 244 | continue 245 | XList.append(X) 246 | YList.append(Y) 247 | caList.append(ca) 248 | flowList.append(flow) 249 | flowNames.append(ca + ' ' + flow) 250 | if xmax != None: 251 | xmin = min(xmin, min(X)) 252 | xmax = max(xmax, max(X)) 253 | ymin = min(ymin, min(Y)) 254 | ymax = max(ymax, max(Y)) 255 | 256 | # print 'xmin:%.2f, xmax:%.2f, ymin:%.2f, ymax:%.2f' % (xmin, xmax, ymin, ymax) 257 | 258 | for X in XList: 259 | i = 0 260 | for x in X: 261 | X[i] = x - xmin 262 | i += 1 263 | if xmax != None: 264 | xmax -= xmin 265 | xmin -= xmin 266 | else: 267 | sys.exit(2) 268 | 269 | #print "Callong addGraphs" 270 | Xsum, Ysum = addGraphs(XList, YList, xmin, xmax) 271 | 272 | if Xsum != None: 273 | L = ['Sum'] 274 | L.extend(flowNames) 275 | flowNames = L 276 | L = [Xsum] 277 | L.extend(XList) 278 | XList = L 279 | L = [Ysum] 280 | L.extend(YList) 281 | YList = L 282 | ymax = max(ymax, max(Ysum)) 283 | firstGraphSum = True 284 | colOffset = 0 285 | else: 286 | firstGraphSum = False 287 | colOffset = 1 288 | 289 | path = os.path.dirname(sys.argv[1]) 290 | 291 | #print "Staring plots" 292 | p = psPlot.PsPlot(path + '/' + 'rates', '', '', 1) 293 | p.SetPlotBgLevel(0.95) 294 | p.SetPlot(xmin, xmax, 0, 0, ymax, 0, 'Time in seconds', 'Goodput Mbps', 295 | 'Goodputs') 296 | p.seriesTitle = 'Flows' 297 | p.SeriesNames(flowNames) 298 | 299 | i = 0 300 | for X in XList: 301 | Y = YList[i] 302 | if len(X) < 2: 303 | continue 304 | inc = X[1] - X[0] 305 | if X[0] > 0: 306 | Xt = [] 307 | Yt = [] 308 | Xt.append(0.0) 309 | Xt.append(X[0] - inc) 310 | Xt.extend(X) 311 | X = Xt 312 | Yt.append(0.0) 313 | Yt.append(0.0) 314 | Yt.extend(Y) 315 | Y = Yt 316 | if X[-1] < xmax: 317 | X.append(X[-1] + inc) 318 | X.append(xmax) 319 | Y.append(0.0) 320 | Y.append(0.0) 321 | if firstGraphSum == True: 322 | p.PlotData(1, X, Y, 323 | # '', '', '2.0 ' + p.SetColor(p.colors[i % p.colorsN]) + 324 | # ' plotStraightLinesC') 325 | '', '', '3 2.0 ' + p.SetColor(p.colors[i % p.colorsN]) + 326 | ' plotNAvgStraightLinesC') 327 | firstGraphSum = False 328 | else: 329 | p.PlotData(1, X, Y, 330 | '', '', '0.4 ' + p.SetColor(p.colors[(i + colOffset) % p.colorsN]) 331 | + ' plotStraightLinesC') 332 | i += 1 333 | 334 | image = p.GetImage() 335 | #print 'Plot: ', image 336 | -------------------------------------------------------------------------------- /netesto/local/processExp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys, random, os.path, commands, getopt 4 | 5 | flog = None 6 | desc = None 7 | otherList = [] 8 | 9 | #--- isFloat 10 | def isFloat(s): 11 | try: return (float(s),True)[1] 12 | except (ValueError, TypeError), e: return False 13 | 14 | #--- putHtml 15 | def putHtml(fout, fieldList, path, fname, link, title): 16 | # fout.write('Content-type: text/html\n\n') 17 | fout.write(' '+title+' '+'\n') 18 | fout.write('\n') 19 | # fout.write(inputLine+'\n') 20 | fout.write('
\n') 21 | fout.write('\n') 22 | fout.write('\n') 23 | fout.write('\n') 24 | fout.write('\n') 25 | fout.write('
\n') 26 | fout.write('\n\n\n') 27 | 28 | #----- putCsvHeader 29 | def putCsvHeader(fout, fieldList): 30 | count = 0 31 | for f in fieldList: 32 | if count == 0: 33 | fout.write(f) 34 | else: 35 | fout.write(','+f) 36 | count += 1 37 | fout.write('\n') 38 | 39 | #--- putCsvRow 40 | def putCsvRow(fout, rowDict, fieldList): 41 | count = 0 42 | for f in fieldList: 43 | if f in rowDict: 44 | v = str(rowDict[f]) 45 | v = v.strip() 46 | if v == 'None': 47 | v = ' ' 48 | else: 49 | v = ' ' 50 | if count == 0: 51 | fout.write(v) 52 | else: 53 | fout.write(','+v) 54 | count += 1 55 | fout.write('\n') 56 | 57 | def getFloat(s): 58 | n = len(s) 59 | if s[n-1:] == 'M': 60 | val = float(s[:n-1]) * 1000000 61 | elif s[n-1:] == 'K': 62 | val = float(s[:n-1]) * 1000 63 | elif s[n-1:] == 'G': 64 | val = float(s[:n-1]) * 1000000000 65 | else: 66 | val = float(s) 67 | return val 68 | 69 | def processRow(fieldList, fieldVal, fieldOpDict): 70 | global desc 71 | global otherList 72 | 73 | row = [] 74 | print >>flog, 'Creating row' 75 | for f in fieldList: 76 | if f in fieldVal: 77 | print >>flog, ' doing '+f 78 | if fieldOpDict[f] == 'avg': 79 | if fieldVal[f][0] == None or fieldVal[f][1] == 0: 80 | row.append(f+'=') 81 | else: 82 | if f != 'retransPkts%': 83 | v = "%.3f" % float(fieldVal[f][0]/fieldVal[f][1]) 84 | else: 85 | v = "%.3f" % float(fieldVal[f][0]/fieldVal[f][1]) 86 | row.append(f+'='+str(v)) 87 | elif fieldOpDict[f] == 'sumRateDur': 88 | if fieldVal[f][0] == None or fieldVal[f][1] == 0: 89 | row.append(f+'=') 90 | else: 91 | v = "%.3f" % float(fieldVal[f][0]/fieldVal[f][1]) 92 | row.append(f+'='+str(v)) 93 | else: 94 | if f == 'desc' and fieldVal[f][0] == None and desc != None: 95 | row.append(f+'='+desc) 96 | else: 97 | breakFlag = False 98 | for other in otherList: 99 | if f == other[0]: 100 | row.append(f+'='+other[1]) 101 | # print "other found: ", f+'='+other[1] 102 | breakFlag = True 103 | break 104 | if breakFlag: 105 | continue 106 | if isFloat(fieldVal[f][0]) and f != 'exp' and \ 107 | f != 'retransPkts%': 108 | s = "%.1f" % float(fieldVal[f][0]) 109 | else: 110 | s = str(fieldVal[f][0]) 111 | if len(s) >= 1 and s[-1] == '/': 112 | s = s[:-1] 113 | row.append(f+'=' + s) 114 | else: 115 | print f, 'Not in fieldVal' 116 | print >>flog, 'Row:', row 117 | return row 118 | 119 | # ------------------------------ Main ------------------ 120 | 121 | action = 'create' 122 | field_fn = 'fields.txt' 123 | #foutName = '/home/brakmo/www/Experiments/nv/hw/1G-1G/1/exp.html' 124 | foutName = '/Library/webServer/Documents/Exp/exp.html' 125 | fname = 'exp' 126 | #path = '/home/brakmo/www/Experiments/nv/hw/1G-1G' 127 | path = '/Library/webServer/Documents/Exp' 128 | relPath = '/Exp' 129 | outPath = '.' 130 | title = 'HW Exp' 131 | rfile = '' 132 | link = 'exp.html' 133 | 134 | fieldList = [] 135 | 136 | opt_list = ['outPath=', 'relPath=', 'path=', 'fname=', 'link=', 'fields=', 'row=', 'title=', 'rfile=', 'desc=', 'other=' ] 137 | 138 | try: 139 | opts, args = getopt.getopt(sys.argv[1:], 'ca', opt_list) 140 | except getopt.GetoptError, err: 141 | print str(err) 142 | sys.exit(-1) 143 | 144 | row = [] 145 | rowList = [] 146 | 147 | for opt in opts: 148 | key = opt[0] 149 | val = opt[1] 150 | if key == '-c': 151 | action = 'create' 152 | elif key == '-a': 153 | action = 'add' 154 | elif key == '--path': 155 | path = val 156 | elif key == '--relPath': 157 | relPath = val 158 | elif key == '--outPath': 159 | outPath = val 160 | elif key == '--fname': 161 | fname = val 162 | elif key == '--link': 163 | link = val 164 | elif key == '--debug': 165 | flog = open(val, 'a') 166 | flog.write('\nNEW FILE\n--------\n') 167 | elif key == '--fields': 168 | field_fn = val 169 | elif key == '--row': 170 | row = val.split(' ') 171 | rowList.append(row) 172 | elif key == '--title': 173 | title = val 174 | elif key == '--rfile': 175 | rfile = val 176 | elif key == '--desc': 177 | desc = val 178 | elif key == '--other': 179 | # print "--ohter="+val 180 | otherList = [] 181 | if val != '': 182 | tmpList = val.split(',') 183 | for e in tmpList: 184 | epair = e.split('=') 185 | otherList.append(epair) 186 | # print "otherList:", otherList 187 | 188 | if flog == None: 189 | flog = open('/dev/null', 'w') 190 | 191 | print >>flog, 'opts = ', opts 192 | print >>flog, 'args = ', args 193 | 194 | #fname_use = open('processExp.use', 'r') 195 | #for iline in fname_use: 196 | # line = iline.strip() 197 | # if len(line) == 0 or line[0] == '#': 198 | # continue 199 | # kv = line.split('=') 200 | # if len(kv) < 2: 201 | # continue 202 | # if kv[0] == 'fname': 203 | # fname = kv[1] 204 | #fname_use.close() 205 | 206 | fieldOpDict = {} 207 | fin = open(field_fn, 'r') 208 | for iline in fin: 209 | line = iline.strip() 210 | if len(line) == 0: 211 | continue 212 | if line[0] != '#': 213 | field = line.split(':') 214 | fieldList.append(field[0]) 215 | fieldOpDict[field[0]] = field[1] 216 | fin.close() 217 | 218 | fieldVal = {} 219 | lineNum = 0 220 | 221 | if rfile != '': 222 | print >>flog, 'rfile:'+rfile 223 | for f in fieldList: 224 | fieldVal[f] = [None, 0] 225 | fin = open(rfile, 'r') 226 | dur = None 227 | maxDur = 0 228 | rate = None 229 | for iline in fin: 230 | line = iline.strip() 231 | if len(line) == 0 or line[0] == '#': 232 | continue 233 | print >>flog, 'LINE:' + line 234 | if line.find('---') >= 0: 235 | rowList.append(processRow(fieldList, fieldVal, fieldOpDict)) 236 | for f in fieldList: 237 | fieldVal[f] = [None, 0] 238 | dur = None 239 | maxDur = 0 240 | rate = None 241 | lineNum += 1 242 | print >>flog, 'Line #:', lineNum 243 | continue 244 | keyval = line.split(':') 245 | if len(keyval) < 2: 246 | continue 247 | key = keyval[0] 248 | val = keyval[1] 249 | 250 | if key == 'dur': 251 | dur = getFloat(val) 252 | if dur > maxDur: 253 | maxDur = dur 254 | if rate != None and 'rate' in fieldOpDict and fieldOpDict['rate'] == 'sumRateDur': 255 | rate *= dur 256 | if fieldVal[key][0] == None: 257 | fieldVal[key][0] = rate 258 | else: 259 | fieldVal[key][0] += rate 260 | rate = None 261 | dur = None 262 | fieldVal[key][1] = maxDur 263 | elif key == 'rate': 264 | rate = getFloat(val) 265 | 266 | print >>flog, 'rfile - key:'+key+' val:'+val 267 | if not key in fieldList: 268 | print >>flog, 'rfile - key not in fieldList' 269 | continue 270 | if key in fieldOpDict: 271 | print >>flog, ' op:'+fieldOpDict[key] 272 | print >>flog, ' 0:'+str(fieldVal[key][0]) 273 | print >>flog, ' 1:'+str(fieldVal[key][1]) 274 | if fieldOpDict[key] != 'one' and val == '': 275 | continue 276 | if fieldOpDict[key] == 'sum': 277 | if fieldVal[key][0] == None: 278 | fieldVal[key][0] = getFloat(val) 279 | else: 280 | fieldVal[key][0] += getFloat(val) 281 | elif fieldOpDict[key] == 'sumRateDur': 282 | if dur != None: 283 | newVal = getFloat(val)*dur 284 | if fieldVal[key][0] == None: 285 | fieldVal[key][0] = newVal 286 | else: 287 | fieldVal[key][0] += newVal 288 | fieldVal[key][1] = maxDur 289 | rate = None 290 | dur = None 291 | elif fieldOpDict[key] == 'all': 292 | if fieldVal[key][0] == None: 293 | fieldVal[key][0] = val + '/' 294 | elif fieldVal[key][0].find(val+'/') < 0: 295 | fieldVal[key][0] += val + '/' 296 | elif fieldOpDict[key] == 'avg': 297 | if fieldVal[key][0] == None: 298 | fieldVal[key][0] = getFloat(val) 299 | else: 300 | fieldVal[key][0] += getFloat(val) 301 | fieldVal[key][1] += 1 302 | elif fieldOpDict[key] == 'max': 303 | if fieldVal[key][0] == None: 304 | fieldVal[key][0] = getFloat(val) 305 | elif float(val) > fieldVal[key][0]: 306 | fieldVal[key][0] = getFloat(val) 307 | elif fieldOpDict[key] == 'one': 308 | if fieldVal[key][0] == None: 309 | fieldVal[key][0] = val 310 | elif val != fieldVal[key][0]: 311 | #print >>flog, 'ERROR: non matching for '+key+': '+str(val)+', '+str(fieldVal[key][0]) 312 | if fieldVal[key][0].find('+...') < 0: 313 | fieldVal[key][0] += '+...' 314 | elif fieldOpDict[key] == 'min': 315 | if fieldVal[key][0] == None: 316 | fieldVal[key][0] = getFloat(val) 317 | elif float(val) < fieldVal[key][0]: 318 | fieldVal[key][0] = getFloat(val) 319 | 320 | fin.close() 321 | 322 | print >>flog, 'Fields' 323 | print >>flog, fieldList 324 | 325 | if action == 'create': 326 | fout_html = open(outPath+'/'+fname+'.html', 'w') 327 | putHtml(fout_html, fieldList, path, fname, link, title) 328 | fout_csv = open(outPath+'/'+fname+'.csv', 'w') 329 | putCsvHeader(fout_csv, fieldList) 330 | 331 | elif action == 'add': 332 | fout_html = open(outPath+'/'+fname+'.html', 'a') 333 | fout_csv = open(outPath+'/'+fname+'.csv', 'a') 334 | 335 | print >>flog, 'len(rowList):', len(rowList) 336 | for row in rowList: 337 | rowDict = {} 338 | if len(row) > 0: 339 | for r in row: 340 | elem = r.split('=') 341 | rowDict[elem[0]] = elem[1] 342 | print >>flog, 'Row' 343 | print >>flog, row 344 | putCsvRow(fout_csv, rowDict, fieldList) 345 | 346 | fout_html.close() 347 | fout_csv.flush() 348 | os.fsync(fout_csv.fileno()) 349 | fout_csv.close() 350 | flog.close() 351 | -------------------------------------------------------------------------------- /netesto/local/processExp.use: -------------------------------------------------------------------------------- 1 | fname=expWeb 2 | -------------------------------------------------------------------------------- /netesto/local/script.23: -------------------------------------------------------------------------------- 1 | # 2 | # Script for testing TCP CAs 3 | # 4 | # Runs 1, 2 or 3 flows 5 | # 6 | # Uses inlib.caTest 7 | # 8 | 9 | # Set default host suffix 10 | HOST_SUFFIX mynetwork.net 11 | 12 | # replace with your own hostnames here or use script.hosts below 13 | SET client1router=server1 14 | SET client2router=server2 15 | SET client3router=server3 16 | 17 | SET server_no_router=server4 18 | SET server_with_router=server5 19 | 20 | # Set router for experiments using router to limit rate and set buffer 21 | SET router=server6 22 | 23 | # Uncomment following line if you have your server names in the 24 | # file script.hosts 25 | #SOURCE script.hosts 26 | 27 | SET clients1=$client1 28 | SET clients2=$client1,$client2 29 | SET clients3=$client1,$client2,$client3 30 | 31 | # Load library with macros 32 | DEBUG_DISABLE 33 | SOURCE inlib.caTest 34 | SOURCE inlib.rateTest 35 | DEBUG_RESTORE 36 | 37 | # set default reply size of RPCs 38 | SET reply=1 # use RPC reply size of 1 byte 39 | 40 | BEGIN QDISC 41 | FOR c IN $clients DO 42 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 43 | SET_QDISC host=$c qdisc=pfifo action=replace 44 | SET_QDISC host=$c qdisc=mq action=replace 45 | DONE 46 | END QDISC 47 | 48 | # 49 | # Define commands to run before each test 50 | # 51 | # On Server(s) 52 | # 53 | BEGIN preServer 54 | # set large receive buffers in server 55 | SET_SYSCTL host=$host net.core.rmem_max=67108864 56 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,33554432 57 | END preServer 58 | # 59 | # On Client(s) 60 | # 61 | BEGIN preClient 62 | # set large send buffers in client 63 | SET_SYSCTL host=$host net.core.wmem_max=67108864 64 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,33554432 65 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 66 | SET_QDISC host=$c qdisc=pfifo action=replace 67 | SET_QDISC host=$c qdisc=mq action=replace 68 | END preClient 69 | 70 | # Set current kernel to associate results with kernels 71 | SET kernel=4.14.0-rc5 72 | 73 | SET baselineCA=cubic # Baseline CA 74 | SET testCA=reno,bbr # CA to test (can be more than one separated 75 | # with commas, no spaces) 76 | SET dur=60 # Duration of individual tests in seconds 77 | SET delay=0 # Netem delay to use in milliseconds 78 | 79 | SET qdisc=fq_codel 80 | RUN QDISC clients=$clients3 qdisc=$qdisc 81 | 82 | # Enable all tcpCA23 tests 83 | SET tcpCA23_baseline=1 84 | SET tcpCA23_test=1 85 | SET tcpCA23_vs1=1 86 | SET tcpCA23_vs2=1 87 | 88 | # For collecting tcpdumps on all hots. The number represents the number of 89 | # packets to collect 90 | #SET tcpDump=10000 91 | 92 | # Use server not going through router 93 | SET server1=$server_no_router 94 | SET server=$server1 95 | SET reps=1 96 | 97 | # *** LAN 10G 2 & 3 flows 98 | # 99 | SET rate=10gbits 100 | SET limit=20000 101 | OTHER testType=tcpCA23,testNote=limit:20000,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$limit 102 | RUN tcpCA23,$reps 103 | 104 | # *** LAN 10G 2 & 3 flows with 10ms delay 105 | # 106 | SET delay=10 107 | SET rate=10gbits 108 | SET limit=20000 109 | OTHER testType=tcpCA23,testNote=limit:20000,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$limit 110 | RUN tcpCA23,$reps 111 | 112 | # *** LAN 10G 2 & 3 flows with 10ms delay and various losses 113 | # *** WARNING: not yet tested 114 | SET delay=10 115 | FOR n IN 1,2,3,4,5 DO 116 | FOR loss IN 0.01,0.1,1.0,2.0 DO 117 | DESC netemLoss:$loss 118 | SET netemLoss=$loss 119 | SET testCA=cubic,bbr,bic 120 | SET tcpCA23_baseline=1 121 | SET tcpCA23_test=1 122 | SET tcpCA23_vs1=0 123 | SET tcpCA23_vs2=0 124 | RUN tcpCA23,$reps 125 | DONE 126 | DONE 127 | 128 | END 129 | -------------------------------------------------------------------------------- /netesto/local/script.23router: -------------------------------------------------------------------------------- 1 | # 2 | # Script for testing TCP CAs 3 | # Tests use an intermediate server as router in order to reduce 4 | # bandwdith and vary the buffer sizes 5 | # 6 | # Runs 1, 2 or 3 flows 7 | # 8 | # Uses inlib.caTest 9 | # 10 | 11 | # Set default host suffix 12 | HOST_SUFFIX mynetwork.net 13 | 14 | # replace with your own hostnames here or use script.hosts below 15 | SET client1router=server1 16 | SET client2router=server2 17 | SET client3router=server3 18 | 19 | SET server_no_router=server4 20 | SET server_with_router=server5 21 | 22 | # Set router for experiments using router to limit rate and set buffer 23 | SET router=server6 24 | 25 | # Uncomment following line if you have your server names in the 26 | # file script.hosts 27 | #SOURCE script.hosts 28 | 29 | SET clients1=$client1router 30 | SET clients2=$client1router,$client2router 31 | SET clients3=$client1router,$client2router,$client3router 32 | 33 | # Load library with macros 34 | DEBUG_DISABLE 35 | SOURCE inlib.caTest 36 | SOURCE inlib.rateTest 37 | DEBUG_RESTORE 38 | 39 | # set default reply size of RPCs 40 | SET reply=1 # use RPC reply size of 1 byte 41 | 42 | # Macro for setting client qdisc 43 | BEGIN QDISC 44 | FOR c IN $clients DO 45 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 46 | SET_QDISC host=$c qdisc=pfifo action=replace 47 | SET_QDISC host=$c qdisc=mq action=replace 48 | DONE 49 | END QDISC 50 | 51 | # 52 | # Define commands to run before each test 53 | # 54 | # On Server(s) 55 | # 56 | BEGIN preServer 57 | # set large receive buffers in server 58 | SET_SYSCTL host=$host net.core.rmem_max=67108864 59 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,33554432 60 | END preServer 61 | # 62 | # On Client(s) 63 | # 64 | BEGIN preClient 65 | # set large send buffers in client 66 | SET_SYSCTL host=$host net.core.wmem_max=67108864 67 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,33554432 68 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 69 | SET_QDISC host=$c qdisc=pfifo action=replace 70 | SET_QDISC host=$c qdisc=mq action=replace 71 | END preClient 72 | 73 | # Set current kernel to associate results with kernels 74 | SET kernel=4.14.0-rc5 75 | 76 | SET baselineCA=cubic # Baseline CA 77 | #SET testCA=reno,bic,bbr # CA to test (can be more than one separated 78 | # with commas, no spaces) 79 | SET dur=60 # Duration of individual tests in seconds 80 | SET delay=40 # Netem delay to use in milliseconds 81 | SET qdisc=fq_codel 82 | RUN QDISC clients=$clients3 qdisc=$qdisc 83 | 84 | # Enable all tcpCA23 tests 85 | SET tcpCA23_baseline=1 86 | SET tcpCA23_test=1 87 | SET tcpCA23_vs1=1 88 | SET tcpCA23_vs2=1 89 | 90 | # For collecting tcpdumps on all hots. The number represents the number of 91 | # packets to collect 92 | #SET tcpDump=10000 93 | 94 | SET server1=$server_with_router 95 | SET server=$server1 96 | SET reps=1 97 | SET delay=40 98 | SET rate=10mbit 99 | SET limit=1400000 100 | 101 | OTHER testType=tcpCA23,testNote=limit:5000,qdisc=$qdisc,netem=$delay,bw=$rate 102 | #RUN tcpCA23,$reps 103 | 104 | 105 | ### *** Tests of 10,40ms RTT and 10,40,100 Mbps using server as router 106 | # Testing 2 & 3 flows with 2nd and 3rd flows starting after earlier flows 107 | # It also does baselineCA vs. testCA 108 | # 109 | SET server1=$server_with_router 110 | SET server=$server1 111 | SET servers1=$server1 112 | 113 | # Set router for experiments using router to limit rate and set buffer 114 | SET router=kerneltest010 115 | 116 | # Run 10 iterations of the tests 117 | FOR n IN 1,2,3,4,5,6,7,8,9,10 DO 118 | 119 | # First set of tests, rate 10mbit 120 | SET rate=10mbit 121 | SET reps=1 122 | SET limits=400000,200000,100000,50000,25000 #in bytes, 8x, 4x,2x,1x,1/2x BDP 123 | SET tcpCA23_vs2=1 # turn off vs2 testing 124 | 125 | FOR limit IN $limits DO 126 | SET_EXP buffers=$limit/1500 127 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit action=replace 128 | OTHER testType=tcpCA23,testNote=40ms,kernel=$kernel,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$buffers,burst=3000 129 | RUN tcpCA23,$reps 130 | DONE 131 | 132 | # Second set of tests, rate 100mbit 133 | SET rate=100mbit 134 | SET reps=1 135 | SET limits=4000000,2000000,1000000,500000,250000 #in bytes, 8x, 4x,2x,1x,1/2x BDP 136 | SET tcpCA23_vs2=1 # turn off vs2 testing 137 | 138 | FOR limit IN $limits DO 139 | SET_EXP buffers=$limit/1500 140 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit action=replace 141 | OTHER testType=tcpCA23,testNote=40ms,kernel=$kernel,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$buffers,burst=3000 142 | RUN tcpCA23,$reps 143 | DONE 144 | 145 | DONE # for the 10 iterations 146 | 147 | END 148 | 149 | 150 | -------------------------------------------------------------------------------- /netesto/local/script.basic: -------------------------------------------------------------------------------- 1 | # 2 | # Sample script 3 | # 4 | 5 | HOST_SUFFIX # Example: mynetwork.com 6 | SOURCE inlib 7 | SET instances=1 # how many flow instances per host 8 | SET reps=1 # how many macro instances to run 9 | SET dur=60 # duration of test in seconds 10 | SET server1= # specify server. Example kerneltest101 11 | SET server2= # specify another server 12 | SET client1= # specify client. Example kerneltest102 13 | SET client2= # specify client. Example kerneltest103 14 | DESC simple_script # specify descripiton 15 | SET ca=cubic # will use TCP-Cubic 16 | SET doRPC=1 # whether to run RPC tests 17 | SET req=1M # use RPC request size of 1MB 18 | # Clients send requests, servers replies 19 | # The tool expects clients to send large requests 20 | # so it collects flow information on the clients 21 | SET reply=1 # use RPC reply size of 1 byte 22 | SET doSTREAM=1 # whether to run STREAM tests 23 | SET randDelay=0.1 # wait between 0 and 0.1 secs before staring flows 24 | # introduces randomness necessary when running the 25 | # same test multiple times 26 | 27 | # 1 client, 1 server, RPC and STREAM 28 | # 29 | IF $doRPC: RUN OneFlowRR,$reps exp=COUNTER server=$server1 client=$client1 expName=1h1fr ca=$ca dur=$dur delay=0 instances=$instances req=$req reply=$reply 30 | 31 | IF $doSTREAM: RUN OneFlowStream,$reps exp=COUNTER server=$server1 client=$client1 expName=1h1fs ca=$ca dur=$dur delay=0 instances=$instances 32 | 33 | # If you want a larger delay (specified in ms), you can use netem on a given host 34 | SET_NETEM host=$client1 netem_delay=100 35 | 36 | # Add values to other columns in exp.csv. This columns must be specified in the file 37 | # "fields.txt". Only change "fields.txt" before you start a new series of experiments; 38 | # that is, when there is no exp.html or exp.csv 39 | OTHER netem=10 40 | 41 | IF $doRPC: RUN OneFlowRR,$reps exp=COUNTER server=$server1 client=$client1 expName=1h1fr ca=$ca dur=$dur delay=0 instances=$instances req=$req reply=$reply 42 | 43 | IF $doSTREAM: RUN OneFlowStream,$reps exp=COUNTER server=$server1 client=$client1 expName=1h1fs ca=$ca dur=$dur delay=0 instances=$instances 44 | 45 | # Reset delay 46 | SET_NETEM host=$client1 netem_delay=100 47 | OTHER netem=0 48 | 49 | # You can also use multiple servers and clients and specify multiple CAs (one test per ca) 50 | RUN MServerRR servers=$server1,$server2 clients=$client1,$client2 expName=2s2c3fr ca=cubic,reno dur=$dur delay=0 instances=$instances reqs=1M,1M,10K reply=$reply 51 | 52 | RUN MServerStream servers=$server1,$server2 clients=$client1,$client2 expName=2s2c1fs ca=cubic,reno dur=$dur delay=0 instances=$instances 53 | 54 | END 55 | -------------------------------------------------------------------------------- /netesto/local/script.ca: -------------------------------------------------------------------------------- 1 | # 2 | # Script for testing TCP CAs 3 | # 4 | # Uses inlib.caTest 5 | # 6 | 7 | # Set default host suffix 8 | HOST_SUFFIX 06.atn1.facebook.com 9 | 10 | # replace with your own hostnames 11 | SET client1=kerneltest002 12 | SET client2=kerneltest004 13 | SET client3=kerneltest005 14 | 15 | SET clients1=$client1 16 | SET clients2=$client1,$client2 17 | SET clients3=$client1,$client2,$client3 18 | 19 | SET server_no_router=kerneltest011 20 | SET server_with_router=kerneltest001 21 | 22 | # Load library with macros 23 | DEBUG_DISABLE 24 | SOURCE inlib.caTest 25 | SOURCE inlib.rateTest 26 | DEBUG_RESTORE 27 | 28 | #OTHER testType=VS_RTT,netem=25,testNote=dctcp_vs_cubic_ecn,ecn=400/1802 29 | #OTHER testType=vs,testNote=4.8.0-rc6-01478-g2d7a892,netem=10,ecn=9k 30 | 31 | # set default reply size of RPCs 32 | SET reply=1 # use RPC reply size of 1 byte 33 | 34 | # 35 | # Define commands to run before each test 36 | # 37 | # On Server(s) 38 | # 39 | BEGIN preServer 40 | # set large receive buffers in server 41 | SET_SYSCTL host=$host net.core.rmem_max=67108864 42 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,33554432 43 | END preServer 44 | # 45 | # On Client(s) 46 | # 47 | BEGIN preClient 48 | # set large send buffers in client 49 | SET_SYSCTL host=$host net.core.wmem_max=67108864 50 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,33554432 51 | END preClient 52 | 53 | SET kernel=4.11.0-rc3 54 | 55 | SET baselineCA=cubic # Baseline CA 56 | SET testCA=bbr # CA to test (can be more than one separated with commas, no spaces) 57 | #SET testCA=reno 58 | SET dur=60 # Duration of individual tests in seconds 59 | SET delay=0 # Netem delay to use in milliseconds 60 | 61 | # Use fs qdisc for senders (test clients) because of bbr 62 | SET qdisc=fq 63 | #SET qdisc=pfifo 64 | FOR c IN $clients3 DO 65 | SET_QDISC host=$c qdisc=$qdisc 66 | DONE 67 | 68 | # Enable all tcpCA23 tests 69 | SET tcpCA23_baseline=1 70 | SET tcpCA23_test=1 71 | SET tcpCA23_vs1=1 72 | SET tcpCA23_vs2=1 73 | 74 | #SET tcpDump=10000 75 | 76 | 77 | # Use server not going through router 78 | SET server1=$server_no_router 79 | SET server=$server1 80 | 81 | # *** LAN 10G 2 & 3 flows 82 | # 83 | SET rate=10gbits 84 | OTHER testType=tcpCA23,testNote=x,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=1000,burst=3000 85 | #RUN tcpCA23 86 | 87 | # *** WAN 10G 10ms 2 & 3 flows 88 | # 89 | SET delay=10 90 | OTHER testType=tcpCA23,testNote=x,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=1000,burst=3000 91 | #RUN tcpCA23 92 | 93 | ### *** Tests of 10,40ms RTT and 5,10,40,100 Mbps using server as router 94 | # Testing 2 & 3 flows with 2nd and 3rd flows starting after earlier flows 95 | # It also does baselineCA vs. testCA 96 | # 97 | SET server1=$server_with_router 98 | SET server=$server1 99 | SET servers1=$server1 100 | 101 | # Set router for experiments using router to limit rate and set buffer 102 | SET router=kerneltest010 103 | 104 | SET delays=10,40 105 | SET rates=5mbit,10mbit,40mbit,100mbit 106 | SET limit=1500000 # in bytes, divide by 1500 for packets 107 | SET_EXP buffers=$limit/1500 108 | 109 | FOR delay IN $delays DO 110 | FOR rate IN $rates DO 111 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit 112 | OTHER testType=tcpCA23,testNote=x,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=$buffers,burst=3000 113 | # RUN tcpCA23 114 | DONE 115 | DONE 116 | 117 | ### *** Tests of 10,40ms RTT and 10 Mbps using server as router 118 | # Testing 2 & 3 flows with 2nd and 3rd flows starting after earlier flows 119 | # Evaluating decreasing buffer size at bottleneck 120 | # 121 | 122 | SET delays=10,40 123 | SET rates=10mbit 124 | SET limits=1500000,750000,375000,187500,96000 # in bytes, divide by 1500 for packets 125 | SET tcpCA23_vs1=0 # turn off vs testing 126 | SET tcpCA23_vs2=0 # turn off vs testing 127 | 128 | FOR limit IN $limits DO 129 | SET_EXP buffers=$limit/1500 130 | FOR delay IN $delays DO 131 | FOR rate IN $rates DO 132 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit 133 | OTHER testType=tcpCA23,testNote=buffers,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=$buffers,burst=3000 134 | RUN tcpCA23 135 | DONE 136 | DONE 137 | DONE 138 | 139 | END 140 | 141 | ### *** Tests of 10,80ms RTT and 10,100 Mbps using server as router 142 | # Testing 1,2,4,8,16 Stream flows from 3 clients into -> 1 server 143 | # 144 | SET server1=$server_with_router 145 | SET server=$server1 146 | SET servers1=$server1 147 | 148 | # Set router for experiments using router to limit rate and set buffer 149 | SET router=kerneltest010 150 | 151 | SET delays=10,80 152 | SET rates=10mbit,100mbit 153 | SET limit=1500000 # in bytes, divide by 1500 for packets 154 | SET_EXP buffers=$limit/1500 155 | 156 | SET rate3s=1 157 | FOR delay IN $delays DO 158 | FOR rate IN $rates DO 159 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit 160 | OTHER testType=3Stream1,testNote=x,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=$buffers,burst=3000 161 | RUN ExpRate servers=$servers1 clients=$clients3 ca=$baselineCA dur=$dur 162 | RUN ExpRate servers=$servers1 clients=$clients3 ca=$testCA dur=$dur 163 | DONE 164 | DONE 165 | 166 | ### *** Tests of 10,80ms RTT and 10,100 Mbps using server as router 167 | # Testing 1,2,4,8,16 x2 RPC flows (1KB and 1MB) from 3 clients into -> 1 server 168 | # 169 | SET server1=$server_with_router 170 | SET server=$server1 171 | SET servers1=$server1 172 | 173 | # Set router for experiments using router to limit rate and set buffer 174 | SET router=kerneltest010 175 | 176 | SET delays=10,80 177 | SET rates=10mbit,100mbit 178 | SET limit=1500000 # in bytes, divide by 1500 for packets 179 | SET_EXP buffers=$limit/1500 180 | 181 | SET rate3s=0 182 | SET rate3p=1 183 | SET delays=10,80 184 | SET rates=10mbit,100mbit 185 | FOR delay IN $delays DO 186 | FOR rate IN $rates DO 187 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit 188 | OTHER testType=3RPC1,testNote=x,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=$buffers,burst=3000 189 | RUN ExpRate servers=$servers1 clients=$clients3 ca=$baselineCA dur=$dur 190 | RUN ExpRate servers=$servers1 clients=$clients3 ca=$testCA dur=$dur 191 | DONE 192 | DONE 193 | 194 | 195 | END 196 | 197 | ### 25x LAN 10G 2 & 3 flows just for test CA to check fairness stability 198 | # Use server not going through router 199 | SET server1=$server_no_router 200 | SET server=$server1 201 | 202 | NEXT_COUNTER 203 | SET delay=0 204 | SET tcpCA23_baseline=0 205 | SET tcpCA23_vs1=0 206 | SET tcpCA23_vs2=0 207 | OTHER testType=tcpCA23,testNote=25,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=1000,burst=3000 208 | RUN tcpCA23,25 209 | 210 | ### 25x WAN 10G 10ms 2 & 3 flows just for test CA to check fairness stability 211 | # Use server not going through router 212 | SET server1=$server_no_router 213 | SET server=$server1 214 | 215 | NEXT_COUNTER 216 | SET delay=10 217 | OTHER testType=tcpCA23,testNote=25,kernel=$kernel,qdisc=$qdisc,netem=$delay,ecn=9k,bw=$rate,buffers=1000,burst=3000 218 | RUN tcpCA23,25 219 | 220 | END 221 | 222 | 223 | -------------------------------------------------------------------------------- /netesto/local/script.complexSample: -------------------------------------------------------------------------------- 1 | # 2 | # Sample script for netesto 3 | # 4 | 5 | # Set default host suffix 6 | HOST_SUFFIX mynetwork.com 7 | 8 | # Set hosts for 1, 2, 3 or 4 client host experiments 9 | # replace with appropriate hostnames 10 | SET clients1=host01 11 | SET clients2=host01,host02 12 | SET clients3=host01,host02,host03 13 | SET clients4=host01,host02,host03,host04 14 | 15 | # Set hosts for 1 or 2 server host experiments 16 | # replace with appropriate hostnames 17 | SET servers1=host05 18 | SET servers2=host05,host06 19 | 20 | # Set congestion control variants to run 21 | SET ca=reno,cubic,nv,dctcp 22 | 23 | # Load library with macros 24 | SOURCE inlib 25 | 26 | SET instances=1 # how many flow instances per host 27 | SET dur=60 # duration of each run in seconds 28 | 29 | # specify descripiton of experiments 30 | DESC ECN_Experiment 31 | 32 | # set default reply size of RPCs 33 | SET reply=1 # use RPC reply size of 1 byte 34 | 35 | END # remove after fixing variables above 36 | 37 | # 38 | # Define commands to run before each test 39 | # 40 | # On Server(s) 41 | # 42 | BEGIN preServer 43 | # set large receive buffers in server 44 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,20971520 45 | END preServer 46 | # 47 | # On Client(s) 48 | # 49 | BEGIN preClient 50 | # set large send buffers in client 51 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,20971520 52 | END preClient 53 | 54 | # Experiments ----------------------------- 55 | # 56 | # By default all will execute. You can disable individually by replacing 57 | # "IF 1:" by "IF 0:" or "IF $varnanme" where varname is defined as an integer 58 | 59 | 60 | # 1 server, 1 client, 1M 61 | SET reqs=1M 62 | IF 1: RUN MServerRR servers=$servers1 clients=$clients1 expName=1s1c1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 63 | 64 | # 1 server, 1 client Stream 65 | IF 1: RUN MServerStream servers=$servers1 clients=$clients1 expName=1s1c1fs ca=$ca dur=$dur delay=0 instances=$instances 66 | 67 | # 1 server, 1 client, 1M, 10K 68 | SET reqs=1M,10K 69 | IF 1: RUN MServerRR servers=$servers1 clients=$clients1 expName=1s1c2fvr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 70 | 71 | # 1 server, 1 client, 1M, 1M 72 | SET reqs=1M,1M 73 | IF 1: RUN MServerRR servers=$servers1 clients=$clients1 expName=1s1c2fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 74 | 75 | # 1 server, 1 client, 2xStream 76 | IF 1: RUN MServerStream servers=$servers1 clients=$clients1 expName=1s1c2fs ca=$ca dur=$dur delay=0 instances=2 77 | 78 | # 2 servers, 2 clients, 1M and 10K RPCs (4 flows per client, 2 for each server) 79 | SET reqs=1M,10K 80 | IF 1: RUN MServerRR servers=$servers2 clients=$clients2 expName=2s2c2fvr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 81 | 82 | # 2 servers, 3 clients, 1M, 1M and 10K RPCs (6 flows per client, 3 for each server) 83 | SET reqs=1M,1M,10K 84 | IF 1: RUN MServerRR servers=$servers2 clients=$clients3 expName=2s3c3fvr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 85 | 86 | # 2 servers, 3 clients, 4x1M and 10K RPCs (10 flows per client, 5 for each server) 87 | SET reqs=1M,1M,1M,1M,10K 88 | RUN MServerRR servers=$servers2 clients=$clients3 expName=2s3c5fvr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 89 | 90 | # 2 servers, 3 clients, 4x1M and 10K RPCs (10 flows per client, 5 for each server) 91 | # 5 minutes duration 92 | SET dur=300 93 | SET reqs=1M,1M,1M,1M,10K 94 | IF 1: RUN MServerRR servers=$servers2 clients=$clients3 expName=2s3c5fr ca=$ca dur=$dur delay=0 instances=$instances reqs=$reqs reply=$reply 95 | SET dur=60 96 | 97 | # 1 server, 4 client 1M 98 | IF 1: RUN MServerRR servers=$servers1 clients=$clients4 expName=1s4c1fr ca=$ca dur=$dur delay=0 instances=$instances reqs=1M reply=1 99 | 100 | # 1 server, 4 client, 2x1M 101 | IF 1: RUN MServerRR servers=$servers1 clients=$clients4 expName=1s4c2fr ca=$ca dur=$dur delay=0 instances=2 reqs=1M reply=1 102 | 103 | # 1 server, 4 client, 4x1M 104 | IF 1: RUN MServerRR servers=$servers1 clients=$clients4 expName=1s4c4fr ca=$ca dur=$dur delay=0 instances=4 reqs=1M reply=1 105 | 106 | # 1 server, 4 client, 8x1M 107 | IF 1: RUN MServerRR servers=$servers1 clients=$clients4 expName=1s4c8fr ca=$ca dur=$dur delay=0 instances=8 reqs=1M reply=1 108 | 109 | # 1 server, 4 client, 16x1M 110 | IF 1: RUN MServerRR servers=$servers1 clients=$clients4 expName=1s4c16fr ca=$ca dur=$dur delay=0 instances=16 reqs=1M reply=1 111 | 112 | # 1 server, 4 client, 4xSTREAM 113 | IF 1: RUN MServerStream servers=$servers1 clients=$clients4 expName=1s4c4fs ca=$ca dur=$dur delay=0 instances=4 114 | 115 | # 1 server, 4 client, 8xSTREAM 116 | IF 1: RUN MServerStream servers=$servers1 clients=$clients4 expName=1s4c8fs ca=$ca dur=$dur delay=0 instances=8 117 | 118 | # 1 server, 4 client, 16xSTREAM 119 | IF 1: RUN MServerStream servers=$servers1 clients=$clients4 expName=1s4c16fs ca=$ca dur=$dur delay=0 instances=16 120 | 121 | END 122 | 123 | -------------------------------------------------------------------------------- /netesto/local/script.rateTest: -------------------------------------------------------------------------------- 1 | # 2 | # Basic rate tests 3 | # 4 | # Consists of the following 21 tests: 5 | # 1) 1->1: 1x1M 1s1c01.fr SET rate1r=1 \_ SET rate1=1 6 | # 2) 1->1: 1XS 1s1c01.fs SET rate1s=1 / 7 | # 3) 2->1: 1x1M 1s2c01.fr SET rate2r1=1 \ 8 | # 4) 2->1: 1xS 1s2c01.fr SET rate2s1=1 | 9 | # 5) 2->1: 2x1M 1s2c02.fr SET rate2r1=1 |- SET rate2=1 10 | # 6) 2->1: 2x16M 1s2c02.fr | 11 | # 7) 2->1: 2x1M,10K 1s2c02.1fr SET rate2r1=1 | 12 | # 8) 2->1: 2x16M,10K 1s2c02.1fr / 13 | # 9) 3->1: 1M 1s3c01.0fr SET rate3r1=1 \ 14 | # 10) 3->1: 2x1M 1s3c02.0fr SET rate3r2=1 | 15 | # 11) 3->1: 4x1M 1s3c04.0fr SET rate3r4=1 |- SET rate3=1 16 | # 12) 3->1: 8x1M runs twice 1s3c08.0fr | 17 | # 13) 3->1: 16x1M runs twice 1s3c16.0fr | 18 | # 14) 3->1: 32x1M runs twice 1s3c32.0fr / 19 | # ) 3->1: 1xSTREAM 1s3c01fs SET rate3s1=1 \ 20 | # ) 3->1: 2xSTREAM 1s3c02fs SET rate3s2=1 | 21 | # 15) 3->1: 4xSTREAM 1s3c04fs SET rate3s4=1 | 22 | # 16) 3->1: 8xSTREAM 1s3c08fs |- SET rate3s=1 23 | # 17) 3->1:16xSTREAM 1s3c16fs / 24 | # 18) 3->1: 1x 10K,1M 1s3c01.pfr SET rate3p1=1 \ 25 | # 19) 3->1: 2x 10K,1M 1s3c02.pfr SET rate3p2=1 | 26 | # 20) 3->1: 4x 10K,1M 1s3c04.pfr SET rate3p4=1 |- SET rate3p=1 27 | # 21) 3->1: 8x 10K,1M 1s3c08.pfr SET rate3p8=1 | 28 | # 22) 3->1: 16x 10K,1M 1s3c16.pfr SET rate3p16=1 | 29 | # 23) 3->1: 32x 10K,1M 1s3c32.pfr SET rate3p32=1 / 30 | # 24) 3->1: 1x 8M,1M,50K,10K 1s3c01.xfr \ 31 | # 25) 3->1: 2x 8M,1M,50K,10K 1s3c02.xfr | 32 | # 26) 3->1: 4x 8M,1M,50K,10K 1s3c04.xfr | 33 | # 27) 3->1: 8x 8M,1M,50K,10K 1s3c08.xfr |- SET rate3x=1 34 | # 28) 3->1: 16x 8M,1M,50K,10K 1s3c16.xfr | 35 | # 29) 3->1: 32x 8M,1M,50K,10K 1s3c32.xfr / 36 | # 37 | 38 | # Set default host suffix 39 | HOST_SUFFIX mynetwork.com 40 | 41 | # Set hosts for 1, 2, 3 or 4 client host experiments 42 | # replace with your own hostnames 43 | SET client1=kerneltest010 44 | SET client2=kerneltest011 45 | SET client3=kerneltest012 46 | SET servers1=kerneltest001 47 | SET ca=dctcp 48 | 49 | SET clients1=$client1 50 | SET clients2=$client1,$client2 51 | SET clients3=$client1,$client2,$client3 52 | 53 | # Load library with macros 54 | DEBUG_DISABLE 55 | SOURCE inlib 56 | SOURCE inlib.rateTest 57 | DEBUG_RESTORE 58 | 59 | SET instances=1 # how many flow instances per host 60 | SET dur=60 # duration of each run in seconds 61 | 62 | # set default reply size of RPCs 63 | SET reply=1 # use RPC reply size of 1 byte 64 | 65 | # 66 | # Define commands to run before each test 67 | # 68 | # On Server(s) 69 | # 70 | BEGIN preServer 71 | # set large receive buffers in server 72 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,20971520 73 | END preServer 74 | # 75 | # On Client(s) 76 | # 77 | BEGIN preClient 78 | # set large send buffers in client 79 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,20971520 80 | END preClient 81 | 82 | # Specify which tests in inlib.rateTest to run 83 | SET rate1=1 84 | SET rate2=1 85 | SET rate3s=1 86 | SET rate3p=1 87 | 88 | RUN ExpRate 89 | 90 | END 91 | 92 | -------------------------------------------------------------------------------- /netesto/local/script.sizes: -------------------------------------------------------------------------------- 1 | # 2 | # Script for testing TCP CAs 3 | # Uses manuy different flows from clients to server 4 | # 5 | 6 | # Set default host suffix 7 | HOST_SUFFIX mynetwork.net 8 | 9 | # replace with your own hostnames here or use script.hosts below 10 | SET client1router=server1 11 | SET client2router=server2 12 | SET client3router=server3 13 | 14 | SET server_no_router=server4 15 | SET server_with_router=server5 16 | 17 | # Set router for experiments using router to limit rate and set buffer 18 | SET router=server6 19 | 20 | # Uncomment following line if you have your server names in the 21 | # file script.hosts 22 | #SOURCE script.hosts 23 | 24 | SET clients1=$client1 25 | SET clients2=$client1,$client2 26 | SET clients3=$client1,$client2,$client3 27 | 28 | # Load library with macros 29 | DEBUG_DISABLE 30 | SOURCE inlib.caTest 31 | SOURCE inlib.rateTest 32 | DEBUG_RESTORE 33 | 34 | # set default reply size of RPCs 35 | SET reply=1 # use RPC reply size of 1 byte 36 | 37 | BEGIN QDISC 38 | FOR c IN $clients DO 39 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 40 | SET_QDISC host=$c qdisc=pfifo action=replace 41 | SET_QDISC host=$c qdisc=mq action=replace 42 | DONE 43 | END QDISC 44 | 45 | # 46 | # Define commands to run before each test 47 | # 48 | # On Server(s) 49 | # 50 | BEGIN preServer 51 | # set large receive buffers in server 52 | SET_SYSCTL host=$host net.core.rmem_max=67108864 53 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,33554432 54 | END preServer 55 | # 56 | # On Client(s) 57 | # 58 | BEGIN preClient 59 | # set large send buffers in client 60 | SET_SYSCTL host=$host net.core.wmem_max=67108864 61 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,33554432 62 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 63 | SET_QDISC host=$c qdisc=pfifo action=replace 64 | SET_QDISC host=$c qdisc=mq action=replace 65 | END preClient 66 | 67 | SET kernel=4.14.0-rc5 68 | 69 | SET ca=cubic,reno,bbr 70 | 71 | SET dur=60 # Duration of individual tests in seconds 72 | SET delay=0 # Netem delay to use in milliseconds 73 | 74 | SET qdisc=fq_codel 75 | RUN QDISC clients=$clients3 qdisc=$qdisc 76 | 77 | # For collecting tcpdumps on all hots. The number represents the number of 78 | # packets to collect 79 | #SET tcpDump=10000 80 | 81 | # Use server not going through router 82 | SET server1=$server_no_router 83 | SET server=$server1 84 | SET reps=1 85 | SET netemLimit=20000 86 | SET netemHost=$server 87 | SET_NETEM host=$netemHost netem_delay=$delay limit=$netemLimit 88 | 89 | # *** LAN 10G various sizes flows 90 | # 91 | SET rate=10gbits 92 | OTHER testType=tcpSizes,testNote=10G-10ms,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$netemLimit 93 | #SET L=1M 94 | #SET S=10K 95 | SET L=8M 96 | SET S=1M 97 | 98 | # Repeat tests 5 times 99 | FOR n IN 1,2,3,4,5 DO 100 | 101 | # 1 host: 3 flows, stream, 1MB, 10KB 102 | RUN MServerStreamRR servers=$server clients=$clients1 expName=1s1c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 103 | 104 | # 3 hosts, 1 flow from each host: stream, 1MB, 10KB 105 | RUN MServerRR_client3_req3 servers=$server clients1=$client1 clients2=$client2 clients3=$client3 expName=1s3c01f.srr ca=$ca dur=$dur delay=0 instances=1 reqs1=S reqs2=$L reqs3=$S reply=1 106 | 107 | # 2 hosts: 3 flows from each: stream, 1MB, 10KB 108 | RUN MServerStreamRR servers=$server clients=$clients2 expName=1s2c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 109 | 110 | # 3 hosts: 3 flows from each: stream, 1MB, 10KB 111 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 112 | 113 | # 3 hosts: 4 flows from each: stream, 2 *1MB, 10KB 114 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c02f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$S reply=1 115 | 116 | # 3 hosts: 6 flows from each: stream, 4 * 1MB, 10KB 117 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c04f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$L,$L,$S reply=1 118 | 119 | # 3 hosts: 10 flows from each: stream, 8 * 1MB, 10KB 120 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c08f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$L,$L,$L,$L,$L,$L,$S reply=1 121 | 122 | # 3 hosts: 18 flows from each: stream, 16 * 1MB, 10KB 123 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c16f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$L,$L,$L,$L,$L,$L,$S reply=1 124 | 125 | DONE 126 | 127 | SET_NETEM host=$server netem_delay=0 128 | 129 | # Run again, but set delay to 10 (ms) 130 | 131 | END 132 | 133 | 134 | -------------------------------------------------------------------------------- /netesto/local/script.sizesrouter: -------------------------------------------------------------------------------- 1 | # 2 | # Script for testing TCP CAs 3 | # Tests use an intermediate server as router in order to reduce 4 | # bandwdith and vary the buffer sizes 5 | # Runs many different flows from clients to server 6 | # 7 | 8 | # Set default host suffix 9 | HOST_SUFFIX mynetwork.net 10 | 11 | # replace with your own hostnames here or use script.hosts below 12 | SET client1router=server1 13 | SET client2router=server2 14 | SET client3router=server3 15 | 16 | SET server_no_router=server4 17 | SET server_with_router=server5 18 | 19 | # Set router for experiments using router to limit rate and set buffer 20 | SET router=server6 21 | 22 | # Uncomment following line if you have your server names in the 23 | # file script.hosts 24 | #SOURCE script.hosts 25 | 26 | SET clients1=$client1router 27 | SET clients2=$client1,router$client2router 28 | SET clients3=$client1router,$client2router,$client3router 29 | 30 | # Load library with macros 31 | DEBUG_DISABLE 32 | SOURCE inlib.caTest 33 | SOURCE inlib.rateTest 34 | DEBUG_RESTORE 35 | 36 | # set default reply size of RPCs 37 | SET reply=1 # use RPC reply size of 1 byte 38 | 39 | BEGIN QDISC 40 | FOR c IN $clients DO 41 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 42 | SET_QDISC host=$c qdisc=pfifo action=replace 43 | SET_QDISC host=$c qdisc=mq action=replace 44 | DONE 45 | END QDISC 46 | 47 | # 48 | # Define commands to run before each test 49 | # 50 | # On Server(s) 51 | # 52 | BEGIN preServer 53 | # set large receive buffers in server 54 | SET_SYSCTL host=$host net.core.rmem_max=67108864 55 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,33554432 56 | END preServer 57 | # 58 | # On Client(s) 59 | # 60 | BEGIN preClient 61 | # set large send buffers in client 62 | SET_SYSCTL host=$host net.core.wmem_max=67108864 63 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,33554432 64 | SET_SYSCTL host=$c net.core.default_qdisc=$qdisc 65 | SET_QDISC host=$c qdisc=pfifo action=replace 66 | SET_QDISC host=$c qdisc=mq action=replace 67 | END preClient 68 | 69 | SET kernel=4.14.0-rc5 70 | 71 | SET ca=cubic,bbr 72 | 73 | SET dur=60 # Duration of individual tests in seconds 74 | SET delay=40 # Netem delay to use in milliseconds 75 | 76 | SET qdisc=fq_codel 77 | RUN QDISC clients=$clients3 qdisc=$qdisc 78 | 79 | # For collecting tcpdumps on all hots. The number represents the number of 80 | # packets to collect 81 | #SET tcpDump=10000 82 | 83 | SET server1=$server_with_router 84 | SET server=$server1 85 | SET servers1=$server1 86 | 87 | # Run tests 10 times 88 | FOR n IN 1,2,3,4,5,6,7,8,9,10 DO 89 | 90 | SET rate=10mbit 91 | SET reps=1 92 | SET limits=400000,50000,25000 #in bytes, 4x,1x,1/2x BDP 93 | 94 | FOR limit IN $limits DO 95 | SET_EXP buffers=$limit/1500 96 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit action=replace 97 | OTHER testType=tcpSizes,testNote=buffers,kernel=$kernel,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$buffers,burst=3000 98 | 99 | SET L=1M 100 | SET S=10K 101 | # SET L=8M 102 | # SET S=1M 103 | 104 | # 1 host: 3 flows, stream, 1MB, 10KB 105 | RUN MServerStreamRR servers=$server clients=$clients1 expName=1s1c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 106 | 107 | # 3 hosts, 1 flow from each host: stream, 1MB, 10KB 108 | RUN MServerRR_client3_req3 servers=$server clients1=$client1 clients2=$client2 clients3=$client3 expName=1s3c01f.srr ca=$ca dur=$dur delay=0 instances=1 reqs1=S reqs2=$L reqs3=$S reply=1 109 | 110 | # 2 hosts: 3 flows from each: stream, 1MB, 10KB 111 | RUN MServerStreamRR servers=$server clients=$clients2 expName=1s2c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 112 | 113 | # 3 hosts: 3 flows from each: stream, 1MB, 10KB 114 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 115 | 116 | # 3 hosts: 6 flows from each: stream, 2 *1MB, 10KB 117 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c02f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$L,$L,$S reply=1 118 | 119 | DONE 120 | 121 | SET rate=100mbit 122 | SET reps=1 123 | SET limits=4000000,500000,250000 #in bytes, 8x,1x,1/2x BDP 124 | 125 | FOR limit IN $limits DO 126 | SET_EXP buffers=$limit/1500 127 | SET_QDISC host=$router qdisc=tbf rate=$rate burst=3000 limit=$limit action=replace 128 | OTHER testType=tcpSizes,testNote=buffers,kernel=$kernel,qdisc=$qdisc,netem=$delay,bw=$rate,buffers=$buffers,burst=3000 129 | 130 | SET L=1M 131 | SET S=10K 132 | # SET L=8M 133 | # SET S=1M 134 | 135 | # 1 host: 3 flows, stream, 1MB, 10KB 136 | RUN MServerStreamRR servers=$server clients=$clients1 expName=1s1c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 137 | 138 | # 3 hosts, 1 flow from each host: stream, 1MB, 10KB 139 | RUN MServerRR_client3_req3 servers=$server clients1=$client1 clients2=$client2 clients3=$client3 expName=1s3c01f.srr ca=$ca dur=$dur delay=0 instances=1 reqs1=S reqs2=$L reqs3=$S reply=1 140 | 141 | # 2 hosts: 3 flows from each: stream, 1MB, 10KB 142 | RUN MServerStreamRR servers=$server clients=$clients2 expName=1s2c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 143 | 144 | # 3 hosts: 3 flows from each: stream, 1MB, 10KB 145 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c01f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$S reply=1 146 | 147 | # 3 hosts: 6 flows from each: stream, 2 *1MB, 10KB 148 | RUN MServerStreamRR servers=$server clients=$clients3 expName=1s3c02f.rs ca=$ca dur=$dur delay=0 instances=1 reqs=$L,$L,$L,$L,$S reply=1 149 | 150 | DONE 151 | 152 | DONE 153 | END 154 | 155 | -------------------------------------------------------------------------------- /netesto/local/script.test: -------------------------------------------------------------------------------- 1 | # 2 | # Test script sript.test 3 | # Runs one Request/Reply test with 1 client and 1 server 4 | # 5 | # Replace values appropritely before running (i.e. things within "<>") 6 | # Run by: ./netesto.py [-d | --debug=netesto.log] < script.test 7 | 8 | HOST_SUFFIX 9 | SOURCE inlib 10 | SET client= 11 | SET server= 12 | RUN MServerRR servers=$server clients=$client expName=1c1s1fr ca=cubic dur=10 \ 13 | instances=1 reqs=1M reply=1 14 | -------------------------------------------------------------------------------- /netesto/local/script.vsTest: -------------------------------------------------------------------------------- 1 | # 2 | # Sample script for netesto 3 | # 4 | 5 | # Set default host suffix 6 | HOST_SUFFIX mynetwork.com 7 | 8 | # Set hosts for 1, 2, 3 or 4 client host experiments 9 | # replace with your own hostnames 10 | SET client1=kerneltest010 11 | SET client2=kerneltest011 12 | SET client3=kerneltest012 13 | 14 | SET clients1=$client1 15 | SET clients2=$client1,$client2 16 | SET clients3=$client1,$client2,$client3 17 | 18 | # Set hosts for 1 or 2 server host experiments 19 | # replace with own host names 20 | SET servers1=kerneltest014 21 | 22 | # Set congestion control variants to run 23 | SET ca1=dctcp 24 | SET ca2=cubic 25 | 26 | # Load library with macros 27 | SOURCE inlib 28 | SOURCE inlib.vsTest 29 | 30 | SET instances=1 # how many flow instances per host 31 | SET dur=60 # duration of each run in seconds 32 | 33 | # specify descripiton of experiments 34 | DESC VS_dctcp:cubic 35 | 36 | # set default reply size of RPCs 37 | SET reply=1 # use RPC reply size of 1 byte 38 | 39 | 40 | # 41 | # Define commands to run before each test 42 | # 43 | # On Server(s) 44 | # 45 | BEGIN preServer 46 | # set large receive buffers in server 47 | SET_SYSCTL host=$host net.ipv4.tcp_rmem=10000,262144,20971520 48 | END preServer 49 | # 50 | # On Client(s) 51 | # 52 | BEGIN preClient 53 | # set large send buffers in client 54 | SET_SYSCTL host=$host net.ipv4.tcp_wmem=10000,262144,20971520 55 | END preClient 56 | 57 | RUN ExpVs 58 | 59 | END 60 | -------------------------------------------------------------------------------- /netesto/remote/clients.txt: -------------------------------------------------------------------------------- 1 | #list of ipv6 addresses that can use the netesto server 2 | -------------------------------------------------------------------------------- /netesto/remote/doServer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exp="ZZ" 4 | order="0" 5 | name="$0" 6 | args="$@" 7 | 8 | hostname=`hostname` 9 | hn=`hostname | grep -o '^[a-z0-9]*'` 10 | kernel=`uname -r` 11 | 12 | #-- Usage 13 | printUsage() { 14 | echo "USAGE: $name --exp=<#> [--order=<#>] [-beg] [-end] [-s] [--start=<#>] [-h]" 15 | echo " Where: --exp Refers to experiment number. A directory will" 16 | echo " will be created to store results" 17 | echo " --beg This is being called at beginning of experiment" 18 | echo " subdirectory '0' will store initial stats" 19 | echo " --end This is being called at end of experiment" 20 | echo " subdirectory '1' will store final stats'" 21 | echo " --start !=0 to start netserver if not already running" 22 | echo " -s start netserver if not already running" 23 | echo " --order ==0 => beg, !=0 => end." 24 | echo "" 25 | } 26 | 27 | #-- getStaticStats 28 | getStaticStats() { 29 | cat /proc/net/snmp > $1/$hn.snmp 30 | cat /proc/net/snmp6 > $1/$hn.snmp6 31 | cat /proc/net/netstat > $1/$hn.netstat 32 | sysctl -a -e 2> /dev/null > $1/$hn.sysctl 33 | top -n 1 -b > $1/$hn.top 34 | ifconfig eth0 > $1/$hn.ifconfig 35 | ethtool -S eth0 > $1/$hn.ethtool-S 36 | ethtool -a eth0 > $1/$hn.ethtool-a 37 | ethtool -c eth0 > $1/$hn.ethtool-c 38 | ethtool -k eth0 > $1/$hn.ethtool-k 39 | echo "exp:$exp" > $1/$hn.info 40 | echo "hn:$hn" >> $1/$hn.info 41 | echo "hostname:$hostname" >> $1/$hn.info 42 | echo "kernel:$kernel" >> $1/$hn.info 43 | } 44 | 45 | #-- processArgs 46 | processArgs() { 47 | for i in $args ; do 48 | case $i in 49 | -e=*|--exp=*) 50 | exp="${i#*=}" 51 | ;; 52 | --order=*) 53 | order="${i#*=}" 54 | ;; 55 | --beg) 56 | order="0" 57 | ;; 58 | --end) 59 | order="1" 60 | ;; 61 | -s) 62 | startServer=1 63 | ;; 64 | --start=*) 65 | startServer="${i#*=}" 66 | ;; 67 | -h|--help) 68 | printUsage 69 | exit 70 | ;; 71 | *) 72 | echo "unknown arg: $i" 73 | ;; 74 | esac 75 | done 76 | } 77 | 78 | order="3" 79 | startServer=0 80 | 81 | processArgs 82 | 83 | #tcpRmem=`sysctl net.ipv4.tcp_rmem | awk '{ print $5 }'` 84 | #if [ "$tcpRmem" -lt 20000000 ] ; then 85 | # sysctl -w net.ipv4.tcp_rmem="4096 262144 20971520" 86 | #fi 87 | # 88 | #tcpWmem=`sysctl net.ipv4.tcp_wmem | awk '{ print $5 }'` 89 | #if [ "$tcpWmem" -lt 20000000 ] ; then 90 | # sysctl -w net.ipv4.tcp_wmem="4096 262144 20971520" 91 | #fi 92 | 93 | if [ "$exp" == "ZZ" ] ; then 94 | echo "ERROR, must specify '--exp=<..>'" 95 | echo "" 96 | exit 97 | fi 98 | 99 | if [ "$order" != "3" ] ; then 100 | if ! [ -a $exp ] ; then 101 | mkdir $exp 102 | fi 103 | 104 | if ! [ -a $exp/$order ] ; then 105 | mkdir $exp/$order 106 | fi 107 | 108 | getStaticStats $exp/$order 109 | fi 110 | 111 | if [ "$startServer" != "0" ] ; then 112 | netserverPid=`ps ax | grep netserver | grep --invert-match "grep" | awk '{ print $1 }'` 113 | if [ "$netserverPid" == "" ] ; then 114 | if [ -a ./netserver ] ; then 115 | ./netserver 116 | else 117 | netserver 118 | fi 119 | sleep 3 120 | fi 121 | fi 122 | 123 | if [ "$order" == "0" ] ; then 124 | # echo "Calling doSs.sh $exp" >> /root/nettest/out 125 | ( sleep 1 ; ./doSs.sh $exp ) & 126 | fi 127 | 128 | 129 | -------------------------------------------------------------------------------- /netesto/remote/doSs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -eq "0" ] ; then 4 | d="." 5 | else 6 | d=$1 7 | fi 8 | 9 | hn=`hostname | grep -o '^[a-z0-9]*'` 10 | 11 | #echo "Doing doSs.sh to $d/ss.$hn" >> /root/nettest/out 12 | 13 | n=0 14 | echo "-----------------" >> $d/ss.$hn 15 | while [ "$n" -lt "30" ] ; do ss -6 -i -t -m -o -e "( sport = :55601 )" >> $d/ss.$hn ; n=$[n+1] ; usleep 200000 ; done 16 | 17 | 18 | -------------------------------------------------------------------------------- /netesto/remote/netperf.fields: -------------------------------------------------------------------------------- 1 | ELAPSED_TIME,THROUGHPUT,THROUGHPUT_UNITS,REQUEST_SIZE,RESPONSE_SIZE,LOCAL_CPU_UTIL,LOCAL_CPU_METHOD,REMOTE_CPU_UTIL,REMOTE_CPU_METHOD,TRANSACTION_RATE,RT_LATENCY,LOCAL_TRANSPORT_RETRANS,REMOTE_TRANSPORT_RETRANS,TRANSPORT_MSS,LOCAL_SEND_THROUGHPUT,REMOTE_RECV_THROUGHPUT,LOCAL_CPU_BIND,LOCAL_CPU_COUNT,SOURCE_PORT,DEST_PORT,LOCAL_BYTES_PER_RECV,LOCAL_BYTES_PER_SEND,LOCAL_BYTES_SENT,LOCAL_BYTES_RECVD,REMOTE_BYTES_SENT,REMOTE_BYTES_RECVD,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,P999_LATENCY,MEAN_LATENCY,LOCAL_CONG_CONTROL,REMOTE_CONG_CONTROL,COMMAND_LINE 2 | -------------------------------------------------------------------------------- /netesto/remote/netstat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys 4 | 5 | def processNames(namesStr): 6 | namePosDict = {} 7 | nameList = [] 8 | pos = 0 9 | names = namesStr.split() 10 | for name in names: 11 | if name.find('xxxx') >= 0: 12 | continue 13 | nameList.append(name) 14 | namePosDict[name] = pos 15 | pos += 1 16 | return namePosDict, nameList 17 | 18 | def processVals(nameList, valuesStr): 19 | valuesDict = {} 20 | valuesList = valuesStr.split() 21 | pos = 0 22 | for value in valuesList: 23 | if value.find('xxxx') >= 0: 24 | continue 25 | valuesDict[nameList[pos]] = value 26 | pos += 1 27 | return valuesDict 28 | 29 | def processOne(namesStr, fieldsDict, valuesStr): 30 | namePosDict, nameList = processNames(namesStr) 31 | nameValDict = processVals(nameList, valuesStr) 32 | 33 | for name in nameList: 34 | if len(fieldsDict) > 0 and not name in fieldsDict: 35 | continue 36 | value = nameValDict[name] 37 | if name.find(':') > 0: 38 | print "\n*** ", name 39 | continue 40 | print '%25s %s' % (name, value) 41 | 42 | 43 | def processDiff(namesStr, fieldsDict, valuesBeforeStr, valuesAfterStr): 44 | namePosDict, nameList = processNames(namesStr) 45 | nameValBeforeDict = processVals(nameList, valuesBeforeStr) 46 | nameValAfterDict = processVals(nameList, valuesAfterStr) 47 | 48 | for name in nameList: 49 | if len(fieldsDict) > 0 and not name in fieldsDict: 50 | continue 51 | valueBefore = nameValBeforeDict[name] 52 | valueAfter = nameValAfterDict[name] 53 | if name.find(':') > 0: 54 | print "\n*** ", name 55 | continue 56 | valueDiff = int(valueAfter) - int(valueBefore) 57 | if valueDiff != 0: 58 | print '%25s %6d' % (name, (valueDiff)) 59 | else: 60 | print '%d' % (valueDiff) 61 | 62 | def usage(): 63 | print "" 64 | print "Usage: netstat.py [-f ] " 65 | print " pretty print netstat output" 66 | print "" 67 | print " netstat.py [-f " 68 | print " pretty print non-zero difference between netstat files" 69 | print "" 70 | print " args:" 71 | print " -f only print fields in list " 72 | print " Example: netstat -f 'TCPHPHits,TCPFullUndo' /proc/net/netstat" 73 | sys.exit(0) 74 | 75 | print "" 76 | argc = len(sys.argv) 77 | 78 | i = 1 79 | inFiles = [] 80 | fieldsDict = {} 81 | while i < argc: 82 | a = sys.argv[i] 83 | if a == '-h' or a == '--help': 84 | usage() 85 | elif a == '-f': 86 | i += 1 87 | if i >= argc: 88 | print "ERROR: field list missing" 89 | usage() 90 | fieldsStr = sys.argv[i] 91 | fields = fieldsStr.split(',') 92 | for f in fields: 93 | fieldsDict[f] = 1 94 | else: 95 | inFiles.append(sys.argv[i]) 96 | i += 1 97 | 98 | if len(inFiles) == 1: 99 | fin = open(inFiles[0], "r") 100 | nameFlag = True 101 | for line in fin: 102 | if nameFlag: 103 | nameStr = line 104 | else: 105 | valStr = line 106 | processOne(nameStr, fieldsDict, valStr) 107 | nameFlag = not nameFlag 108 | else: 109 | fin1 = open(inFiles[0], "r") 110 | fin2 = open(inFiles[1], "r") 111 | nameFlag = True 112 | nameStrList = [] 113 | valStrList1 = [] 114 | valStrList2 = [] 115 | for line in fin1: 116 | if nameFlag: 117 | nameStrList.append(line) 118 | else: 119 | valStrList1.append(line) 120 | nameFlag = not nameFlag 121 | nameFlag = True 122 | for line in fin2: 123 | if not nameFlag: 124 | valStrList2.append(line) 125 | nameFlag = not nameFlag 126 | i = 0 127 | for nameStr in nameStrList: 128 | processDiff(nameStr, fieldsDict, valStrList1[i], valStrList2[i]) 129 | i += 1 130 | 131 | print "" 132 | -------------------------------------------------------------------------------- /netesto/remote/setParam.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # ./setParam.sh 4 | 5 | echo $3 > /sys/module/$1/parameters/$2 6 | 7 | -------------------------------------------------------------------------------- /netesto/remote/setSysctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # ./setSysctl.sh 4 | 5 | 6 | sysctl -q -w $1="$2" 7 | 8 | --------------------------------------------------------------------------------