├── .coveragerc ├── .gitattributes ├── .gitignore ├── .travis.yml ├── ChangeLog ├── LICENSE ├── MANIFEST.in ├── README.rst ├── lttng-analyses-record ├── lttng-cputop ├── lttng-iolatencyfreq ├── lttng-iolatencystats ├── lttng-iolatencytop ├── lttng-iolog ├── lttng-iousagetop ├── lttng-irqfreq ├── lttng-irqlog ├── lttng-irqstats ├── lttng-memtop ├── lttng-periodfreq ├── lttng-periodlog ├── lttng-periodstats ├── lttng-periodtop ├── lttng-schedfreq ├── lttng-schedlog ├── lttng-schedstats ├── lttng-schedtop ├── lttng-syscallstats ├── lttng-track-process ├── lttnganalyses ├── __init__.py ├── _version.py ├── cli │ ├── __init__.py │ ├── command.py │ ├── cputop.py │ ├── io.py │ ├── irq.py │ ├── memtop.py │ ├── mi.py │ ├── period_parsing.py │ ├── periods.py │ ├── progressbar.py │ ├── sched.py │ ├── syscallstats.py │ └── termgraph.py ├── common │ ├── __init__.py │ ├── format_utils.py │ ├── parse_utils.py │ ├── time_utils.py │ ├── trace_utils.py │ └── version_utils.py ├── core │ ├── __init__.py │ ├── analysis.py │ ├── cputop.py │ ├── event.py │ ├── io.py │ ├── irq.py │ ├── memtop.py │ ├── period.py │ ├── periods.py │ ├── sched.py │ ├── stats.py │ └── syscalls.py └── linuxautomaton │ ├── __init__.py │ ├── automaton.py │ ├── block.py │ ├── io.py │ ├── irq.py │ ├── mem.py │ ├── net.py │ ├── sched.py │ ├── sp.py │ ├── statedump.py │ ├── sv.py │ └── syscalls.py ├── mit-license.txt ├── parser_generator.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── tests ├── __init__.py ├── common │ ├── __init__.py │ ├── test_format_utils.py │ ├── test_parse_utils.py │ ├── test_trace_utils.py │ └── utils.py └── integration │ ├── __init__.py │ ├── analysis_test.py │ ├── expected │ ├── cputop.txt │ ├── disable_intersect.txt │ ├── iolatencytop.txt │ ├── iousagetop.txt │ ├── irqlog.txt │ ├── irqstats.txt │ └── no_intersection.txt │ ├── gen_ctfwriter.py │ ├── test_cputop.py │ ├── test_intersect.py │ ├── test_io.py │ ├── test_irq.py │ └── trace_writer.py ├── tests_long_regression ├── __init__.py └── test_all_options.py ├── tox.ini └── versioneer.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = lttnganalyses 4 | omit = lttnganalyses/_version.py 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | lttnganalyses/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Backup files 2 | *.~ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # Distribution / packaging 9 | bin/ 10 | build/ 11 | develop-eggs/ 12 | dist/ 13 | eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | MANIFEST 23 | 24 | # Installer logs 25 | pip-log.txt 26 | pip-delete-this-directory.txt 27 | 28 | # Unit test / coverage reports 29 | .tox/ 30 | .coverage 31 | .cache 32 | nosetests.xml 33 | coverage.xml 34 | 35 | # Translations 36 | *.mo 37 | 38 | # Sphinx documentation 39 | docs/_build/ 40 | 41 | proc.db 42 | *~ 43 | \#*\# 44 | .\#* 45 | tags 46 | *.swp 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | sudo: required 3 | dist: trusty 4 | env: 5 | matrix: 6 | - TOXENV=py3 7 | - TOXENV=noutf8 8 | - TOXENV=pep8 9 | # - TOXENV=longregression # long regression is long 10 | before_install: 11 | - sudo apt-add-repository -y ppa:lttng/ppa 12 | - sudo apt-get update 13 | - sudo apt-get install -y python3-setuptools python-virtualenv python3-babeltrace babeltrace 14 | - sudo apt-get purge python3-lxc 15 | install: 16 | - virtualenv --system-site-packages -p python3 .venv 17 | - . .venv/bin/activate 18 | - python3 setup.py install 19 | - pip install codecov 20 | - pip install tox 21 | script: 22 | - tox 23 | - test -d .tox/$TOXENV/log && cat .tox/$TOXENV/log/*.log || true 24 | cache: 25 | directories: 26 | - .tox/$TOXENV 27 | - $HOME/.cache/pip 28 | before_cache: 29 | - rm -f .tox/$TOXENV/log/*.log 30 | after_success: 31 | - codecov -e TOXENV 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | LTTng-Analyses - Licensing 2 | 3 | These analyses are released under the MIT license. This license is used to 4 | allow the use of these analyses in both free and proprietary software. See 5 | mit-license.txt for details. 6 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | recursive-include tests * 3 | include ChangeLog 4 | include LICENSE 5 | include mit-license.txt 6 | include requirements.txt 7 | include test-requirements.txt 8 | include tox.ini 9 | include lttng-cputop 10 | include lttng-iolatencyfreq 11 | include lttng-iolatencystats 12 | include lttng-iolatencytop 13 | include lttng-iolog 14 | include lttng-iousagetop 15 | include lttng-irqfreq 16 | include lttng-irqlog 17 | include lttng-irqstats 18 | include lttng-memtop 19 | include lttng-periodfreq 20 | include lttng-periodlog 21 | include lttng-periodstats 22 | include lttng-periodtop 23 | include lttng-schedfreq 24 | include lttng-schedlog 25 | include lttng-schedstats 26 | include lttng-schedtop 27 | include lttng-syscallstats 28 | -------------------------------------------------------------------------------- /lttng-analyses-record: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # Helper to setup a local LTTng tracing session with the appropriate 26 | # settings for the lttng analyses scripts 27 | 28 | SESSION_NAME="lttng-analysis-$RANDOM" 29 | 30 | destroy() 31 | { 32 | lttng destroy $SESSION_NAME >/dev/null 33 | echo "" 34 | echo "You can now launch the analyses scripts on /$TRACEPATH" 35 | exit 0 36 | } 37 | 38 | if test "$1" = "-h" -o "$1" = "--help"; then 39 | echo "usage : $0" 40 | exit 0 41 | fi 42 | 43 | pgrep -u root lttng-sessiond >/dev/null 44 | if test $? != 0; then 45 | echo "Starting lttng-sessiond as root (trying sudo, start manually if \ 46 | it fails)" 47 | sudo lttng-sessiond -d 48 | if test $? != 0; then 49 | exit 1 50 | fi 51 | fi 52 | 53 | SUDO="" 54 | groups|grep tracing >/dev/null 55 | if test $? != 0; then 56 | echo "You are not a member of the tracing group, so you need root \ 57 | access, the script will try with sudo" 58 | SUDO="sudo" 59 | fi 60 | 61 | # check if lttng command if in the path 62 | # check if the user can execute the command (with sudo if not in tracing group) 63 | # check if lttng-modules is installed 64 | $SUDO lttng list -k | grep sched_switch >/dev/null 65 | if test $? != 0; then 66 | echo "Something went wrong executing \"$SUDO lttng list -k | grep sched_switch\", \ 67 | try to fix the problem manually and then start the script again" 68 | fi 69 | 70 | # if our random session name was already in use, add more randomness... 71 | $SUDO lttng list | grep $SESSION_NAME 72 | if test $? = 0; then 73 | SESSION_NAME="$SESSION_NAME-$RANDOM" 74 | fi 75 | $SUDO lttng list | grep $SESSION_NAME 76 | if test $? = 0; then 77 | echo "Cannot create a random session name, something must be wrong" 78 | exit 2 79 | fi 80 | 81 | lttng create $SESSION_NAME >/tmp/lttngout 82 | [[ $? != 0 ]] && exit 2 83 | TRACEPATH=$(grep Traces /tmp/lttngout | cut -d'/' -f2-) 84 | rm /tmp/lttngout 85 | 86 | trap "destroy" SIGINT SIGTERM 87 | 88 | lttng enable-channel -k chan1 --subbuf-size=8M >/dev/null 89 | 90 | # events that always work 91 | lttng enable-event -s $SESSION_NAME -k sched_switch,sched_wakeup,sched_waking,block_rq_complete,block_rq_issue,block_bio_remap,block_bio_backmerge,netif_receive_skb,net_dev_xmit,sched_process_fork,sched_process_exec,lttng_statedump_process_state,lttng_statedump_file_descriptor,lttng_statedump_block_device,mm_vmscan_wakeup_kswapd,mm_page_free,mm_page_alloc,block_dirty_buffer,irq_handler_entry,irq_handler_exit,softirq_entry,softirq_exit,softirq_raise,irq_softirq_entry,irq_softirq_exit,irq_softirq_raise,kmem_mm_page_alloc,kmem_mm_page_free -c chan1 >/dev/null 92 | [[ $? != 0 ]] && echo "Warning: some events were not enabled, some analyses might not be complete" 93 | 94 | # events that might fail on specific kernels and that are not mandatory 95 | lttng enable-event -s $SESSION_NAME -k writeback_pages_written -c chan1 >/dev/null 2>&1 96 | [[ $? != 0 ]] && echo "Warning: Optional event writeback_pages_written could not be enabled, everything will still work (experimental feature)" 97 | 98 | lttng enable-event -s $SESSION_NAME -k -c chan1 --syscall -a >/dev/null 99 | [[ $? != 0 ]] && exit 2 100 | # if you want to add Perf counters, do something like that : 101 | #lttng add-context -s $SESSION_NAME -k -t perf:cache-misses -t perf:major-faults -t perf:branch-load-misses >/dev/null 102 | 103 | lttng start $SESSION_NAME >/dev/null 104 | [[ $? != 0 ]] && exit 2 105 | 106 | echo -n "The trace is now recording, press ctrl+c to stop it " 107 | 108 | while true; do 109 | echo -n "." 110 | sleep 1 111 | done 112 | 113 | destroy 114 | -------------------------------------------------------------------------------- /lttng-cputop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import cputop 26 | 27 | if __name__ == '__main__': 28 | cputop.run() 29 | -------------------------------------------------------------------------------- /lttng-iolatencyfreq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import io 26 | 27 | 28 | if __name__ == '__main__': 29 | io.runfreq() 30 | -------------------------------------------------------------------------------- /lttng-iolatencystats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import io 26 | 27 | 28 | if __name__ == '__main__': 29 | io.runstats() 30 | -------------------------------------------------------------------------------- /lttng-iolatencytop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import io 26 | 27 | 28 | if __name__ == '__main__': 29 | io.runlatencytop() 30 | -------------------------------------------------------------------------------- /lttng-iolog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import io 26 | 27 | 28 | if __name__ == '__main__': 29 | io.runlog() 30 | -------------------------------------------------------------------------------- /lttng-iousagetop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import io 26 | 27 | 28 | if __name__ == '__main__': 29 | io.runusage() 30 | -------------------------------------------------------------------------------- /lttng-irqfreq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import irq 26 | 27 | 28 | if __name__ == '__main__': 29 | irq.runfreq() 30 | -------------------------------------------------------------------------------- /lttng-irqlog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import irq 26 | 27 | 28 | if __name__ == '__main__': 29 | irq.runlog() 30 | -------------------------------------------------------------------------------- /lttng-irqstats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import irq 26 | 27 | 28 | if __name__ == '__main__': 29 | irq.runstats() 30 | -------------------------------------------------------------------------------- /lttng-memtop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import memtop 26 | 27 | 28 | if __name__ == '__main__': 29 | memtop.run() 30 | -------------------------------------------------------------------------------- /lttng-periodfreq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Antoine Busque 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import periods 26 | 27 | if __name__ == '__main__': 28 | periods.runfreq() 29 | -------------------------------------------------------------------------------- /lttng-periodlog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import periods 26 | 27 | if __name__ == '__main__': 28 | periods.runlog() 29 | -------------------------------------------------------------------------------- /lttng-periodstats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import periods 26 | 27 | if __name__ == '__main__': 28 | periods.runstats() 29 | -------------------------------------------------------------------------------- /lttng-periodtop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import periods 26 | 27 | if __name__ == '__main__': 28 | periods.runtop() 29 | -------------------------------------------------------------------------------- /lttng-schedfreq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Antoine Busque 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import sched 26 | 27 | if __name__ == '__main__': 28 | sched.runfreq() 29 | -------------------------------------------------------------------------------- /lttng-schedlog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import sched 26 | 27 | if __name__ == '__main__': 28 | sched.runlog() 29 | -------------------------------------------------------------------------------- /lttng-schedstats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import sched 26 | 27 | if __name__ == '__main__': 28 | sched.runstats() 29 | -------------------------------------------------------------------------------- /lttng-schedtop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import sched 26 | 27 | if __name__ == '__main__': 28 | sched.runtop() 29 | -------------------------------------------------------------------------------- /lttng-syscallstats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | from lttnganalyses.cli import syscallstats 26 | 27 | 28 | if __name__ == '__main__': 29 | syscallstats.run() 30 | -------------------------------------------------------------------------------- /lttnganalyses/__init__.py: -------------------------------------------------------------------------------- 1 | """TODO""" 2 | 3 | from ._version import get_versions 4 | __version__ = get_versions()['version'] 5 | del get_versions 6 | -------------------------------------------------------------------------------- /lttnganalyses/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /lttnganalyses/cli/memtop.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 2015 - Philippe Proulx 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import operator 26 | from .command import Command 27 | from ..core import memtop 28 | from . import mi 29 | from . import termgraph 30 | 31 | 32 | class Memtop(Command): 33 | _DESC = """The memtop command.""" 34 | _ANALYSIS_CLASS = memtop.Memtop 35 | _MI_TITLE = 'Top memory usage' 36 | _MI_DESCRIPTION = 'Per-TID top allocated/freed memory' 37 | _MI_TAGS = [mi.Tags.MEMORY, mi.Tags.TOP] 38 | _MI_TABLE_CLASS_ALLOCD = 'allocd' 39 | _MI_TABLE_CLASS_FREED = 'freed' 40 | _MI_TABLE_CLASS_TOTAL = 'total' 41 | _MI_TABLE_CLASS_SUMMARY = 'summary' 42 | _MI_TABLE_CLASSES = [ 43 | ( 44 | _MI_TABLE_CLASS_ALLOCD, 45 | 'Per-TID top allocated memory', [ 46 | ('process', 'Process', mi.Process), 47 | ('pages', 'Allocated pages', mi.Number, 'pages'), 48 | ] 49 | ), 50 | ( 51 | _MI_TABLE_CLASS_FREED, 52 | 'Per-TID top freed memory', [ 53 | ('process', 'Process', mi.Process), 54 | ('pages', 'Freed pages', mi.Number, 'pages'), 55 | ] 56 | ), 57 | ( 58 | _MI_TABLE_CLASS_TOTAL, 59 | 'Total allocated/freed memory', [ 60 | ('allocd', 'Total allocated pages', mi.Number, 'pages'), 61 | ('freed', 'Total freed pages', mi.Number, 'pages'), 62 | ] 63 | ), 64 | ( 65 | _MI_TABLE_CLASS_SUMMARY, 66 | 'Memory usage - summary', [ 67 | ('time_range', 'Time range', mi.TimeRange), 68 | ('allocd', 'Total allocated pages', mi.Number, 'pages'), 69 | ('freed', 'Total freed pages', mi.Number, 'pages'), 70 | ] 71 | ), 72 | ] 73 | 74 | def _analysis_tick(self, period_data, end_ns): 75 | if period_data is None: 76 | return 77 | 78 | begin_ns = period_data.period.begin_evt.timestamp 79 | allocd_table = self._get_per_tid_allocd_result_table(period_data, 80 | begin_ns, end_ns) 81 | freed_table = self._get_per_tid_freed_result_table(period_data, 82 | begin_ns, end_ns) 83 | total_table = self._get_total_result_table(period_data, 84 | begin_ns, end_ns) 85 | 86 | if self._mi_mode: 87 | self._mi_append_result_table(allocd_table) 88 | self._mi_append_result_table(freed_table) 89 | self._mi_append_result_table(total_table) 90 | else: 91 | self._print_date(begin_ns, end_ns) 92 | self._print_per_tid_allocd(allocd_table) 93 | self._print_per_tid_freed(freed_table) 94 | self._print_total(total_table) 95 | 96 | def _create_summary_result_tables(self): 97 | total_tables = self._mi_get_result_tables(self._MI_TABLE_CLASS_TOTAL) 98 | begin = total_tables[0].timerange.begin.value 99 | end = total_tables[-1].timerange.end.value 100 | summary_table = \ 101 | self._mi_create_result_table(self._MI_TABLE_CLASS_SUMMARY, 102 | begin, end) 103 | 104 | for total_table in total_tables: 105 | total_allocd = total_table.rows[0].allocd 106 | total_freed = total_table.rows[0].freed 107 | summary_table.append_row( 108 | time_range=total_table.timerange, 109 | allocd=total_allocd, 110 | freed=total_freed, 111 | ) 112 | 113 | self._mi_clear_result_tables() 114 | self._mi_append_result_table(summary_table) 115 | 116 | def _get_per_tid_attr_result_table(self, period_data, table_class, attr, 117 | begin_ns, end_ns): 118 | result_table = self._mi_create_result_table(table_class, 119 | begin_ns, end_ns) 120 | count = 0 121 | 122 | for tid in sorted(period_data.tids.values(), 123 | key=operator.attrgetter(attr), 124 | reverse=True): 125 | result_table.append_row( 126 | process=mi.Process(tid.comm, tid=tid.tid), 127 | pages=mi.Number(getattr(tid, attr)), 128 | ) 129 | count += 1 130 | 131 | if self._args.limit > 0 and count >= self._args.limit: 132 | break 133 | 134 | return result_table 135 | 136 | def _get_per_tid_allocd_result_table(self, period_data, begin_ns, end_ns): 137 | return self._get_per_tid_attr_result_table(period_data, 138 | self._MI_TABLE_CLASS_ALLOCD, 139 | 'allocated_pages', 140 | begin_ns, end_ns) 141 | 142 | def _get_per_tid_freed_result_table(self, period_data, begin_ns, end_ns): 143 | return self._get_per_tid_attr_result_table(period_data, 144 | self._MI_TABLE_CLASS_FREED, 145 | 'freed_pages', 146 | begin_ns, end_ns) 147 | 148 | def _get_total_result_table(self, period_data, begin_ns, end_ns): 149 | result_table = self._mi_create_result_table(self._MI_TABLE_CLASS_TOTAL, 150 | begin_ns, end_ns) 151 | alloc = 0 152 | freed = 0 153 | 154 | for tid in period_data.tids.values(): 155 | alloc += tid.allocated_pages 156 | freed += tid.freed_pages 157 | 158 | result_table.append_row( 159 | allocd=mi.Number(alloc), 160 | freed=mi.Number(freed), 161 | ) 162 | 163 | return result_table 164 | 165 | def _print_per_tid_result(self, result_table, title): 166 | graph = termgraph.BarGraph( 167 | title=title, 168 | unit='pages', 169 | get_value=lambda row: row.pages.value, 170 | get_label=lambda row: '%s (%d)' % (row.process.name, 171 | row.process.tid), 172 | label_header='Process', 173 | data=result_table.rows 174 | ) 175 | 176 | graph.print_graph() 177 | 178 | def _print_per_tid_allocd(self, result_table): 179 | self._print_per_tid_result(result_table, 'Per-TID Memory Allocations') 180 | 181 | def _print_per_tid_freed(self, result_table): 182 | self._print_per_tid_result(result_table, 183 | 'Per-TID Memory Deallocations') 184 | 185 | def _print_total(self, result_table): 186 | alloc = result_table.rows[0].allocd.value 187 | freed = result_table.rows[0].freed.value 188 | print('\nTotal memory usage:\n- %d pages allocated\n- %d pages freed' % 189 | (alloc, freed)) 190 | 191 | def _add_arguments(self, ap): 192 | Command._add_proc_filter_args(ap) 193 | Command._add_top_args(ap) 194 | 195 | 196 | def _run(mi_mode): 197 | memtopcmd = Memtop(mi_mode=mi_mode) 198 | memtopcmd.run() 199 | 200 | 201 | # entry point (human) 202 | def run(): 203 | _run(mi_mode=False) 204 | 205 | 206 | # entry point (MI) 207 | def run_mi(): 208 | _run(mi_mode=True) 209 | -------------------------------------------------------------------------------- /lttnganalyses/cli/progressbar.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import os 24 | import sys 25 | import time 26 | from . import mi 27 | from ..common import format_utils 28 | 29 | 30 | try: 31 | from progressbar import ETA, Bar, Percentage, ProgressBar 32 | progressbar_available = True 33 | except ImportError: 34 | progressbar_available = False 35 | 36 | 37 | # approximation for the progress bar 38 | _BYTES_PER_EVENT = 30 39 | 40 | 41 | def get_folder_size(folder): 42 | total_size = os.path.getsize(folder) 43 | for item in os.listdir(folder): 44 | itempath = os.path.join(folder, item) 45 | if os.path.isfile(itempath): 46 | total_size += os.path.getsize(itempath) 47 | elif os.path.isdir(itempath): 48 | total_size += get_folder_size(itempath) 49 | return total_size 50 | 51 | 52 | class _Progress: 53 | def __init__(self, ts_begin, ts_end, path, use_size=False): 54 | if ts_begin is None or ts_end is None or use_size: 55 | size = get_folder_size(path) 56 | self._maxval = size / _BYTES_PER_EVENT 57 | self._use_time = False 58 | else: 59 | self._maxval = ts_end - ts_begin 60 | self._ts_begin = ts_begin 61 | self._ts_end = ts_end 62 | self._use_time = True 63 | 64 | self._at = 0 65 | self._event_count = 0 66 | self._last_event_count_check = 0 67 | self._last_time_check = time.time() 68 | 69 | def update(self, event): 70 | self._event_count += 1 71 | 72 | if self._use_time: 73 | self._at = event.timestamp - self._ts_begin 74 | else: 75 | self._at = self._event_count 76 | 77 | if self._at > self._maxval: 78 | self._at = self._maxval 79 | 80 | if self._event_count - self._last_event_count_check >= 101: 81 | self._last_event_count_check = self._event_count 82 | now = time.time() 83 | 84 | if now - self._last_time_check >= .1: 85 | self._update_progress() 86 | self._last_time_check = now 87 | 88 | def _update_progress(self): 89 | pass 90 | 91 | def finalize(self): 92 | pass 93 | 94 | 95 | class FancyProgressBar(_Progress): 96 | def __init__(self, ts_begin, ts_end, path, use_size): 97 | super().__init__(ts_begin, ts_end, path, use_size) 98 | self._pbar = None 99 | 100 | if progressbar_available: 101 | widgets = ['Processing the trace: ', Percentage(), ' ', 102 | Bar(marker='#', left='[', right=']'), 103 | ' ', ETA(), ' '] # see docs for other options 104 | self._pbar = ProgressBar(widgets=widgets, 105 | maxval=self._maxval) 106 | self._pbar.start() 107 | else: 108 | print('Warning: progressbar module not available, ' 109 | 'using --no-progress.', file=sys.stderr) 110 | 111 | def _update_progress(self): 112 | if self._pbar is None: 113 | return 114 | 115 | self._pbar.update(self._at) 116 | 117 | def finalize(self): 118 | if self._pbar is None: 119 | return 120 | 121 | self._pbar.finish() 122 | 123 | 124 | class MiProgress(_Progress): 125 | def __init__(self, ts_begin, ts_end, path, use_size): 126 | super().__init__(ts_begin, ts_end, path, use_size) 127 | 128 | if self._use_time: 129 | fmt = 'Starting analysis from {} to {}' 130 | begin = format_utils.format_timestamp(self._ts_begin) 131 | end = format_utils.format_timestamp(self._ts_end) 132 | msg = fmt.format(begin, end) 133 | else: 134 | msg = 'Starting analysis: {} estimated events'.format(round( 135 | self._maxval)) 136 | 137 | mi.print_progress(0, msg) 138 | 139 | def _update_progress(self): 140 | if self._at == self._maxval: 141 | mi.print_progress(1, 'Done!') 142 | return 143 | 144 | if self._use_time: 145 | ts_at = self._at + self._ts_begin 146 | at_ts = format_utils.format_timestamp(ts_at) 147 | end = format_utils.format_timestamp(self._ts_end) 148 | msg = '{}/{}; {} events processed'.format(at_ts, end, 149 | self._event_count) 150 | else: 151 | msg = '{} events processed'.format(self._event_count) 152 | 153 | mi.print_progress(round(self._at / self._maxval, 4), msg) 154 | 155 | def finalize(self): 156 | mi.print_progress(1, 'Done!') 157 | -------------------------------------------------------------------------------- /lttnganalyses/cli/termgraph.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from collections import namedtuple 24 | 25 | 26 | GraphDatum = namedtuple('GraphDatum', ['value', 'value_str']) 27 | BarGraphDatum = namedtuple('BarGraphDatum', ['value', 'value_str', 'label']) 28 | FreqGraphDatum = namedtuple( 29 | 'FreqGraphDatum', ['value', 'value_str', 'lower_bound'] 30 | ) 31 | 32 | 33 | class Graph(): 34 | MAX_GRAPH_WIDTH = 80 35 | BAR_CHAR = '█' 36 | HR_CHAR = '#' 37 | 38 | def __init__(self, data, get_value, get_value_str, title, unit): 39 | self._data = data 40 | self._get_value = get_value 41 | self._title = title 42 | self._unit = unit 43 | self._max_value = 0 44 | self._max_value_len = 0 45 | 46 | if get_value_str is not None: 47 | self._get_value_str_cb = get_value_str 48 | else: 49 | self._get_value_str_cb = Graph._get_value_str_default 50 | 51 | def _transform_data(self, data): 52 | graph_data = [] 53 | 54 | for datum in data: 55 | graph_datum = self._get_graph_datum(datum) 56 | 57 | if graph_datum.value > self._max_value: 58 | self._max_value = graph_datum.value 59 | if len(graph_datum.value_str) > self._max_value_len: 60 | self._max_value_len = len(graph_datum.value_str) 61 | 62 | graph_data.append(graph_datum) 63 | 64 | return graph_data 65 | 66 | def _get_value_str(self, value): 67 | return self._get_value_str_cb(value) 68 | 69 | def _get_graph_datum(self, datum): 70 | value = self._get_value(datum) 71 | value_str = self._get_value_str(value) 72 | 73 | return GraphDatum(value, value_str) 74 | 75 | def _print_header(self): 76 | if self._title: 77 | print(self._title) 78 | 79 | def _print_separator(self): 80 | print(self.HR_CHAR * self.MAX_GRAPH_WIDTH) 81 | 82 | def _print_body(self): 83 | raise NotImplementedError() 84 | 85 | def print_graph(self): 86 | if not self._data: 87 | return 88 | 89 | self._print_header() 90 | self._print_separator() 91 | self._print_body() 92 | print() 93 | 94 | @staticmethod 95 | def _get_value_str_default(value): 96 | if isinstance(value, float): 97 | value_str = '{:0.02f}'.format(value) 98 | else: 99 | value_str = str(value) 100 | 101 | return value_str 102 | 103 | 104 | class BarGraph(Graph): 105 | def __init__(self, data, get_value, get_label, get_value_str=None, 106 | title=None, label_header=None, unit=None): 107 | super().__init__(data, get_value, get_value_str, title, unit) 108 | 109 | self._get_label = get_label 110 | self._label_header = label_header 111 | self._data = self._transform_data(self._data) 112 | 113 | def _get_graph_datum(self, datum): 114 | value = self._get_value(datum) 115 | value_str = self._get_value_str(value) 116 | label = self._get_label(datum) 117 | 118 | return BarGraphDatum(value, value_str, label) 119 | 120 | def _get_value_str(self, value): 121 | value_str = super()._get_value_str(value) 122 | if self._unit: 123 | value_str += ' ' + self._unit 124 | 125 | return value_str 126 | 127 | def _get_graph_header(self): 128 | if not self._label_header: 129 | return self._title 130 | 131 | title_len = len(self._title) 132 | space_width = ( 133 | self.MAX_GRAPH_WIDTH - title_len + 1 + self._max_value_len + 1 134 | ) 135 | 136 | return self._title + ' ' * space_width + self._label_header 137 | 138 | def _print_header(self): 139 | header = self._get_graph_header() 140 | print(header) 141 | 142 | def _get_bar_str(self, datum): 143 | if self._max_value == 0: 144 | bar_width = 0 145 | else: 146 | bar_width = int(self.MAX_GRAPH_WIDTH * datum.value / 147 | self._max_value) 148 | space_width = self.MAX_GRAPH_WIDTH - bar_width 149 | bar_str = self.BAR_CHAR * bar_width + ' ' * space_width 150 | 151 | return bar_str 152 | 153 | def _print_body(self): 154 | for datum in self._data: 155 | bar_str = self._get_bar_str(datum) 156 | value_padding = ' ' * (self._max_value_len - len(datum.value_str)) 157 | print(bar_str, value_padding + datum.value_str, datum.label) 158 | 159 | 160 | class FreqGraph(Graph): 161 | LOWER_BOUND_WIDTH = 8 162 | 163 | def __init__(self, data, get_value, get_lower_bound, 164 | get_value_str=None, title=None, unit=None): 165 | super().__init__(data, get_value, get_value_str, title, unit) 166 | 167 | self._get_lower_bound = get_lower_bound 168 | self._data = self._transform_data(self._data) 169 | 170 | def _get_graph_datum(self, datum): 171 | value = self._get_value(datum) 172 | value_str = self._get_value_str(value) 173 | lower_bound = self._get_lower_bound(datum) 174 | 175 | return FreqGraphDatum(value, value_str, lower_bound) 176 | 177 | def _print_header(self): 178 | header = self._title 179 | if self._unit: 180 | header += ' ({})'.format(self._unit) 181 | 182 | print(header) 183 | 184 | def _get_bar_str(self, datum): 185 | max_width = self.MAX_GRAPH_WIDTH - self.LOWER_BOUND_WIDTH 186 | if self._max_value == 0: 187 | bar_width = 0 188 | else: 189 | bar_width = int(max_width * datum.value / self._max_value) 190 | space_width = max_width - bar_width 191 | bar_str = self.BAR_CHAR * bar_width + ' ' * space_width 192 | 193 | return bar_str 194 | 195 | def _print_body(self): 196 | for datum in self._data: 197 | bound_str = FreqGraph._get_bound_str(datum) 198 | bar_str = self._get_bar_str(datum) 199 | value_padding = ' ' * (self._max_value_len - len(datum.value_str)) 200 | print(bound_str, bar_str, value_padding + datum.value_str) 201 | 202 | @staticmethod 203 | def _get_bound_str(datum): 204 | return '{:>7.03f}'.format(datum.lower_bound) 205 | -------------------------------------------------------------------------------- /lttnganalyses/common/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /lttnganalyses/common/format_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import math 24 | import socket 25 | import struct 26 | import time 27 | from .time_utils import NSEC_PER_SEC 28 | 29 | 30 | def format_size(size, binary_prefix=True): 31 | """Convert an integral number of bytes to a human-readable string. 32 | 33 | Args: 34 | size (int): a non-negative number of bytes. 35 | 36 | binary_prefix (bool, optional): whether to use binary units 37 | prefixes, over SI prefixes (default: True). 38 | 39 | Returns: 40 | The formatted string comprised of the size and units. 41 | 42 | Raises: 43 | ValueError: if size < 0. 44 | """ 45 | if size < 0: 46 | raise ValueError('Cannot format negative size') 47 | 48 | if binary_prefix: 49 | base = 1024 50 | units = [' B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] 51 | else: 52 | base = 1000 53 | units = [' B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] 54 | 55 | if size == 0: 56 | exponent = 0 57 | else: 58 | exponent = int(math.log(size, base)) 59 | if exponent >= len(units): 60 | # Don't try and use a unit above YiB/YB 61 | exponent = len(units) - 1 62 | 63 | size /= base ** exponent 64 | 65 | unit = units[exponent] 66 | 67 | if exponent == 0: 68 | # Don't display fractions of a byte 69 | format_str = '{:0.0f} {}' 70 | else: 71 | format_str = '{:0.2f} {}' 72 | 73 | return format_str.format(size, unit) 74 | 75 | 76 | def format_prio_list(prio_list): 77 | """Format a list of prios into a string of unique prios with count. 78 | 79 | Args: 80 | prio_list (list): a list of PrioEvent objects. 81 | 82 | Returns: 83 | The formatted string containing the unique priorities and 84 | their count if they occurred more than once. 85 | """ 86 | prio_count = {} 87 | prio_str = None 88 | 89 | for prio_event in prio_list: 90 | prio = prio_event.prio 91 | if prio not in prio_count: 92 | prio_count[prio] = 0 93 | 94 | prio_count[prio] += 1 95 | 96 | for prio in sorted(prio_count.keys()): 97 | count = prio_count[prio] 98 | if count > 1: 99 | count_str = ' ({} times)'.format(count) 100 | else: 101 | count_str = '' 102 | 103 | if prio_str is None: 104 | prio_str = '[{}{}'.format(prio, count_str) 105 | else: 106 | prio_str += ', {}{}'.format(prio, count_str) 107 | 108 | if prio_str is None: 109 | prio_str = '[]' 110 | else: 111 | prio_str += ']' 112 | 113 | return prio_str 114 | 115 | 116 | def format_timestamp(timestamp, print_date=False, gmt=False): 117 | """Format a timestamp into a human-readable date string 118 | 119 | Args: 120 | timestamp (int): nanoseconds since epoch. 121 | 122 | print_date (bool, optional): flag indicating whether to print 123 | the full date or just the time of day (default: False). 124 | 125 | gmt (bool, optional): flag indicating whether the timestamp is 126 | in the local timezone or gmt (default: False). 127 | 128 | Returns: 129 | The formatted date string, containing either the full date or 130 | just the time of day. 131 | """ 132 | date_fmt = '{:04}-{:02}-{:02} ' 133 | time_fmt = '{:02}:{:02}:{:02}.{:09}' 134 | 135 | if gmt: 136 | date = time.gmtime(timestamp // NSEC_PER_SEC) 137 | else: 138 | date = time.localtime(timestamp // NSEC_PER_SEC) 139 | 140 | formatted_ts = time_fmt.format( 141 | date.tm_hour, date.tm_min, date.tm_sec, 142 | timestamp % NSEC_PER_SEC 143 | ) 144 | 145 | if print_date: 146 | date_str = date_fmt.format(date.tm_year, date.tm_mon, date.tm_mday) 147 | formatted_ts = date_str + formatted_ts 148 | 149 | return formatted_ts 150 | 151 | 152 | def format_time_range(begin_ts, end_ts, print_date=False, gmt=False): 153 | """Format a pair of timestamps into a human-readable date string. 154 | 155 | Args: 156 | begin_ts (int): nanoseconds since epoch to beginning of 157 | time range. 158 | 159 | end_ts (int): nanoseconds since epoch to end of time range. 160 | 161 | print_date (bool, optional): flag indicating whether to print 162 | the full date or just the time of day (default: False). 163 | 164 | gmt (bool, optional): flag indicating whether the timestamp is 165 | in the local timezone or gmt (default: False). 166 | 167 | Returns: 168 | The formatted dates string, containing either the full date or 169 | just the time of day, enclosed within square brackets and 170 | delimited by a comma. 171 | """ 172 | time_range_fmt = '[{}, {}]' 173 | 174 | begin_str = format_timestamp(begin_ts, print_date, gmt) 175 | end_str = format_timestamp(end_ts, print_date, gmt) 176 | 177 | return time_range_fmt.format(begin_str, end_str) 178 | 179 | 180 | def format_ipv4(ip, port=None): 181 | """Format an ipv4 address into a human-readable string. 182 | 183 | Args: 184 | ip (varies): the ip address as extracted in an LTTng event. 185 | Either an integer or a list of integers, depending on the 186 | tracer version. 187 | 188 | port (int, optional): the port number associated with the 189 | address. 190 | 191 | Returns: 192 | The formatted string containing the ipv4 address and, optionally, 193 | the port number. 194 | 195 | """ 196 | # depending on the version of lttng-modules, the v4addr is an 197 | # integer (< 2.6) or sequence (>= 2.6) 198 | try: 199 | ip_str = '{}.{}.{}.{}'.format(ip[0], ip[1], ip[2], ip[3]) 200 | except TypeError: 201 | # The format string '!I' tells pack to interpret ip as a 202 | # packed structure of network-endian 32-bit unsigned integers, 203 | # which inet_ntoa can then convert into the formatted string 204 | ip_str = socket.inet_ntoa(struct.pack('!I', ip)) 205 | 206 | if port is not None: 207 | ip_str += ':{}'.format(port) 208 | 209 | return ip_str 210 | -------------------------------------------------------------------------------- /lttnganalyses/common/time_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | NSEC_PER_SEC = 1000000000 24 | -------------------------------------------------------------------------------- /lttnganalyses/common/trace_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import time 24 | import datetime 25 | import subprocess 26 | import sys 27 | from .version_utils import Version 28 | from .time_utils import NSEC_PER_SEC 29 | 30 | 31 | BT_INTERSECT_VERSION = Version(1, 4, 0) 32 | 33 | 34 | def is_multi_day_trace_collection_bt_1_3_2(collection, handles=None): 35 | """is_multi_day_trace_collection for BT < 1.3.3. 36 | 37 | Args: 38 | collection (TraceCollection): a babeltrace TraceCollection 39 | instance. 40 | 41 | handles (TraceHandle): a babeltrace TraceHandle instance. 42 | 43 | Returns: 44 | True if the trace collection spans more than one day, 45 | False otherwise. 46 | """ 47 | 48 | time_begin = None 49 | 50 | for handle in handles.values(): 51 | if time_begin is None: 52 | time_begin = time.localtime(handle.timestamp_begin / NSEC_PER_SEC) 53 | year_begin = time_begin.tm_year 54 | month_begin = time_begin.tm_mon 55 | day_begin = time_begin.tm_mday 56 | 57 | time_end = time.localtime(handle.timestamp_end / NSEC_PER_SEC) 58 | year_end = time_end.tm_year 59 | month_end = time_end.tm_mon 60 | day_end = time_end.tm_mday 61 | 62 | if year_begin != year_end: 63 | return True 64 | elif month_begin != month_end: 65 | return True 66 | elif day_begin != day_end: 67 | return True 68 | 69 | return False 70 | 71 | 72 | def is_multi_day_trace_collection(collection, handles=None): 73 | """Check whether a trace collection spans more than one day. 74 | 75 | Args: 76 | collection (TraceCollection): a babeltrace TraceCollection 77 | instance. 78 | handles (TraceHandle): a babeltrace TraceHandle instance. 79 | 80 | Returns: 81 | True if the trace collection spans more than one day, 82 | False otherwise. 83 | """ 84 | 85 | # Circumvent a bug in Babeltrace < 1.3.3 86 | if collection.timestamp_begin is None or \ 87 | collection.timestamp_end is None: 88 | return is_multi_day_trace_collection_bt_1_3_2(collection, 89 | handles) 90 | 91 | date_begin = datetime.date.fromtimestamp( 92 | collection.timestamp_begin // NSEC_PER_SEC 93 | ) 94 | date_end = datetime.date.fromtimestamp( 95 | collection.timestamp_end // NSEC_PER_SEC 96 | ) 97 | 98 | return date_begin != date_end 99 | 100 | 101 | def get_trace_collection_date(collection, handles=None): 102 | """Get a trace collection's date. 103 | 104 | Args: 105 | collection (TraceCollection): a babeltrace TraceCollection 106 | instance. 107 | 108 | handles (TraceHandle): a babeltrace TraceHandle instance. 109 | 110 | Returns: 111 | A datetime.date object corresponding to the date at which the 112 | trace collection was recorded. 113 | 114 | handles (TraceHandle): a babeltrace TraceHandle instance. 115 | 116 | Raises: 117 | ValueError: if the trace collection spans more than one day. 118 | """ 119 | if is_multi_day_trace_collection(collection, handles): 120 | raise ValueError('Trace collection spans multiple days') 121 | 122 | trace_date = datetime.date.fromtimestamp( 123 | collection.timestamp_begin // NSEC_PER_SEC 124 | ) 125 | 126 | return trace_date 127 | 128 | 129 | def get_syscall_name(event): 130 | """Get the name of a syscall from an event. 131 | 132 | Args: 133 | event (Event): an instance of a babeltrace Event for a syscall 134 | entry. 135 | 136 | Returns: 137 | The name of the syscall, stripped of any superfluous prefix. 138 | 139 | Raises: 140 | ValueError: if the event is not a syscall event. 141 | """ 142 | name = event.name 143 | 144 | if name.startswith('sys_'): 145 | return name[4:] 146 | elif name.startswith('syscall_entry_'): 147 | return name[14:] 148 | else: 149 | raise ValueError('Not a syscall event') 150 | 151 | 152 | def read_babeltrace_version(): 153 | try: 154 | output = subprocess.check_output('babeltrace') 155 | except subprocess.CalledProcessError: 156 | raise ValueError('Could not run babeltrace to verify version') 157 | 158 | output = output.decode(sys.stdout.encoding) 159 | first_line = output.splitlines()[0] 160 | version_string = first_line.split()[-1] 161 | 162 | return Version.new_from_string(version_string) 163 | 164 | 165 | def check_field_exists(handles, ev_name, field_name): 166 | """Validate that a field exists in the metadata. 167 | 168 | Args: 169 | handles (TraceHandle): an array of babeltrace TraceHandle instance. 170 | 171 | ev_name (String): the event name in which the field must exist. 172 | 173 | field_name (String): the field that we are looking for. 174 | 175 | Returns: 176 | True if the field is found in the event, False if the field is not 177 | found in the event, or if the event is not found. 178 | """ 179 | for handle in handles.values(): 180 | for event in handle.events: 181 | if event.name == ev_name: 182 | for field in event.fields: 183 | if field.name == field_name: 184 | return True 185 | return False 186 | 187 | 188 | def check_event_exists(handles, name): 189 | """Validate that an event exists in the metadata. 190 | 191 | Args: 192 | handles (TraceHandle): an array of babeltrace TraceHandle instance. 193 | 194 | name (String): the event name in which the field must exist. 195 | 196 | Returns: 197 | True if the event is found in the metadata, False otherwise. 198 | """ 199 | for handle in handles.values(): 200 | for event in handle.events: 201 | if event.name == name: 202 | return True 203 | return False 204 | -------------------------------------------------------------------------------- /lttnganalyses/common/version_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import re 24 | from functools import total_ordering 25 | 26 | 27 | @total_ordering 28 | class Version: 29 | def __init__(self, major, minor, patch, extra=None): 30 | self.major = major 31 | self.minor = minor 32 | self.patch = patch 33 | self.extra = extra 34 | 35 | def __lt__(self, other): 36 | if self.major < other.major: 37 | return True 38 | if self.major > other.major: 39 | return False 40 | 41 | if self.minor < other.minor: 42 | return True 43 | if self.minor > other.minor: 44 | return False 45 | 46 | return self.patch < other.patch 47 | 48 | def __eq__(self, other): 49 | return ( 50 | self.major == other.major and 51 | self.minor == other.minor and 52 | self.patch == other.patch 53 | ) 54 | 55 | def __repr__(self): 56 | version_str = '{}.{}.{}'.format(self.major, self.minor, self.patch) 57 | if self.extra: 58 | version_str += self.extra 59 | 60 | return version_str 61 | 62 | @classmethod 63 | def new_from_string(cls, string): 64 | version_match = re.match(r'(\d+)\.(\d+)\.(\d+)(.*)', string) 65 | 66 | if version_match is None: 67 | major = minor = patch = 0 68 | extra = '+unknown' 69 | else: 70 | major = int(version_match.group(1)) 71 | minor = int(version_match.group(2)) 72 | patch = int(version_match.group(3)) 73 | extra = version_match.group(4) 74 | 75 | return cls(major, minor, patch, extra) 76 | -------------------------------------------------------------------------------- /lttnganalyses/core/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /lttnganalyses/core/cputop.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import stats 25 | from .analysis import Analysis, PeriodData 26 | 27 | 28 | class _PeriodData(PeriodData): 29 | def __init__(self): 30 | self.period_begin_ts = None 31 | self.cpus = {} 32 | self.tids = {} 33 | 34 | 35 | class Cputop(Analysis): 36 | def __init__(self, state, conf): 37 | notification_cbs = { 38 | 'sched_migrate_task': self._process_sched_migrate_task, 39 | 'sched_switch_per_cpu': self._process_sched_switch_per_cpu, 40 | 'sched_switch_per_tid': self._process_sched_switch_per_tid, 41 | 'prio_changed': self._process_prio_changed, 42 | } 43 | 44 | super().__init__(state, conf, notification_cbs) 45 | 46 | def _create_period_data(self): 47 | return _PeriodData() 48 | 49 | def _begin_period_cb(self, period_data): 50 | period = period_data.period 51 | period_data.period_begin_ts = period.begin_evt.timestamp 52 | 53 | def _end_period_cb(self, period_data, completed, begin_captures, 54 | end_captures): 55 | self._compute_stats(period_data) 56 | 57 | def _compute_stats(self, period_data): 58 | """Compute usage stats relative to a certain time range 59 | 60 | For each CPU and process tracked by the analysis, we set its 61 | usage_percent attribute, which represents the percentage of 62 | usage time for the given CPU or process relative to the full 63 | duration of the time range. Do note that we need to know the 64 | timestamps and not just the duration, because if a CPU or a 65 | process is currently busy, we use the end timestamp to add 66 | the partial results of the currently running task to the usage 67 | stats. 68 | """ 69 | duration = self.last_event_ts - period_data.period.begin_evt.timestamp 70 | 71 | for cpu_id in period_data.cpus: 72 | cpu = period_data.cpus[cpu_id] 73 | if cpu.current_task_start_ts is not None: 74 | cpu.total_usage_time += self.last_event_ts - \ 75 | cpu.current_task_start_ts 76 | 77 | cpu.compute_stats(duration) 78 | 79 | for tid in period_data.tids: 80 | proc = period_data.tids[tid] 81 | if proc.last_sched_ts is not None: 82 | proc.total_cpu_time += self.last_event_ts - \ 83 | proc.last_sched_ts 84 | 85 | proc.compute_stats(duration) 86 | 87 | def _process_sched_switch_per_cpu(self, period_data, **kwargs): 88 | timestamp = kwargs['timestamp'] 89 | cpu_id = kwargs['cpu_id'] 90 | wakee_proc = kwargs['wakee_proc'] 91 | 92 | if not self._filter_cpu(cpu_id): 93 | return 94 | 95 | if cpu_id not in period_data.cpus: 96 | period_data.cpus[cpu_id] = CpuUsageStats(cpu_id) 97 | period_data.cpus[cpu_id].current_task_start_ts = \ 98 | period_data.period_begin_ts 99 | 100 | cpu = period_data.cpus[cpu_id] 101 | if cpu.current_task_start_ts is not None: 102 | cpu.total_usage_time += timestamp - cpu.current_task_start_ts 103 | 104 | if not self._filter_process(wakee_proc): 105 | cpu.current_task_start_ts = None 106 | else: 107 | cpu.current_task_start_ts = timestamp 108 | 109 | def _process_sched_switch_per_tid(self, period_data, **kwargs): 110 | cpu_id = kwargs['cpu_id'] 111 | wakee_proc = kwargs['wakee_proc'] 112 | timestamp = kwargs['timestamp'] 113 | prev_tid = kwargs['prev_tid'] 114 | next_tid = kwargs['next_tid'] 115 | next_comm = kwargs['next_comm'] 116 | prev_comm = kwargs['prev_comm'] 117 | 118 | if not self._filter_cpu(cpu_id): 119 | return 120 | 121 | if prev_tid not in period_data.tids: 122 | period_data.tids[prev_tid] = ProcessCpuStats( 123 | None, prev_tid, prev_comm) 124 | prev_proc = period_data.tids[prev_tid] 125 | # Set the last_sched_ts to the beginning of the period 126 | # since we missed the entry event. 127 | prev_proc.last_sched_ts = period_data.period_begin_ts 128 | 129 | prev_proc = period_data.tids[prev_tid] 130 | if prev_proc.last_sched_ts is not None: 131 | prev_proc.total_cpu_time += timestamp - prev_proc.last_sched_ts 132 | prev_proc.last_sched_ts = None 133 | 134 | # Only filter on wakee_proc after finalizing the prev_proc 135 | # accounting 136 | if not self._filter_process(wakee_proc): 137 | return 138 | 139 | if next_tid not in period_data.tids: 140 | period_data.tids[next_tid] = ProcessCpuStats(None, 141 | next_tid, next_comm) 142 | period_data.tids[next_tid].update_prio(timestamp, wakee_proc.prio) 143 | 144 | next_proc = period_data.tids[next_tid] 145 | next_proc.last_sched_ts = timestamp 146 | 147 | def _process_sched_migrate_task(self, period_data, **kwargs): 148 | cpu_id = kwargs['cpu_id'] 149 | proc = kwargs['proc'] 150 | tid = proc.tid 151 | 152 | if not self._filter_process(proc): 153 | return 154 | if not self._filter_cpu(cpu_id): 155 | return 156 | 157 | if tid not in period_data.tids: 158 | period_data.tids[tid] = ProcessCpuStats.new_from_process(proc) 159 | 160 | period_data.tids[tid].migrate_count += 1 161 | 162 | def _process_prio_changed(self, period_data, **kwargs): 163 | timestamp = kwargs['timestamp'] 164 | prio = kwargs['prio'] 165 | tid = kwargs['tid'] 166 | 167 | if tid not in period_data.tids: 168 | return 169 | 170 | period_data.tids[tid].update_prio(timestamp, prio) 171 | 172 | def _filter_process(self, proc): 173 | # Exclude swapper 174 | if proc.tid == 0: 175 | return False 176 | 177 | return super()._filter_process(proc) 178 | 179 | 180 | class CpuUsageStats(): 181 | def __init__(self, cpu_id): 182 | self.cpu_id = cpu_id 183 | # Usage time and start timestamp are in nanoseconds (ns) 184 | self.total_usage_time = 0 185 | self.current_task_start_ts = None 186 | self.usage_percent = None 187 | 188 | def compute_stats(self, duration): 189 | if duration != 0: 190 | self.usage_percent = self.total_usage_time * 100 / duration 191 | else: 192 | self.usage_percent = 0 193 | 194 | def reset(self): 195 | self.total_usage_time = 0 196 | self.usage_percent = None 197 | 198 | 199 | class ProcessCpuStats(stats.Process): 200 | def __init__(self, pid, tid, comm): 201 | super().__init__(pid, tid, comm) 202 | 203 | # CPU Time and timestamp in nanoseconds (ns) 204 | self.total_cpu_time = 0 205 | self.last_sched_ts = None 206 | self.migrate_count = 0 207 | self.usage_percent = None 208 | 209 | def compute_stats(self, duration): 210 | if duration != 0: 211 | self.usage_percent = self.total_cpu_time * 100 / duration 212 | else: 213 | self.usage_percent = 0 214 | 215 | def reset(self): 216 | super().reset() 217 | self.total_cpu_time = 0 218 | self.migrate_count = 0 219 | self.usage_percent = None 220 | -------------------------------------------------------------------------------- /lttnganalyses/core/event.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Philippe Proulx 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import babeltrace as bt 24 | import collections 25 | 26 | 27 | _CTF_SCOPES = ( 28 | bt.CTFScope.EVENT_FIELDS, 29 | bt.CTFScope.EVENT_CONTEXT, 30 | bt.CTFScope.STREAM_EVENT_CONTEXT, 31 | bt.CTFScope.STREAM_EVENT_HEADER, 32 | bt.CTFScope.STREAM_PACKET_CONTEXT, 33 | bt.CTFScope.TRACE_PACKET_HEADER, 34 | ) 35 | 36 | 37 | # This class has an interface which is compatible with the 38 | # babeltrace.reader.Event class. This is the result of a deep copy 39 | # performed by LTTng analyses. 40 | class Event(collections.abc.Mapping): 41 | def __init__(self, bt_ev): 42 | self._copy_bt_event(bt_ev) 43 | 44 | def _copy_bt_event(self, bt_ev): 45 | self._name = bt_ev.name 46 | self._cycles = bt_ev.cycles 47 | self._timestamp = bt_ev.timestamp 48 | self._fields = {} 49 | 50 | for scope in _CTF_SCOPES: 51 | self._fields[scope] = {} 52 | 53 | for field_name in bt_ev.field_list_with_scope(scope): 54 | field_value = bt_ev.field_with_scope(field_name, scope) 55 | self._fields[scope][field_name] = field_value 56 | 57 | @property 58 | def name(self): 59 | return self._name 60 | 61 | @property 62 | def cycles(self): 63 | return self._cycles 64 | 65 | @property 66 | def timestamp(self): 67 | return self._timestamp 68 | 69 | @property 70 | def handle(self): 71 | raise NotImplementedError() 72 | 73 | @property 74 | def trace_collection(self): 75 | raise NotImplementedError() 76 | 77 | def _get_first_field(self, field_name): 78 | for scope_fields in self._fields.values(): 79 | if field_name in scope_fields: 80 | return scope_fields[field_name] 81 | 82 | def field_with_scope(self, field_name, scope): 83 | if scope not in self._fields: 84 | raise ValueError('Invalid scope provided') 85 | 86 | if field_name in self._fields[scope]: 87 | return self._fields[scope][field_name] 88 | 89 | def field_list_with_scope(self, scope): 90 | if scope not in self._fields: 91 | raise ValueError('Invalid scope provided') 92 | 93 | return list(self._fields[scope].keys()) 94 | 95 | def __getitem__(self, field_name): 96 | field = self._get_first_field(field_name) 97 | 98 | if field is None: 99 | raise KeyError(field_name) 100 | 101 | return field 102 | 103 | def __iter__(self): 104 | for key in self.keys(): 105 | yield key 106 | 107 | def __len__(self): 108 | count = 0 109 | 110 | for scope_fields in self._fields.values(): 111 | count += len(scope_fields) 112 | 113 | return count 114 | 115 | def __contains__(self, field_name): 116 | return self._get_first_field(field_name) is not None 117 | 118 | def keys(self): 119 | keys = [] 120 | 121 | for scope_fields in self._fields.values(): 122 | keys += list(scope_fields.keys()) 123 | 124 | return keys 125 | 126 | def get(self, field_name, default=None): 127 | field = self._get_first_field(field_name) 128 | 129 | if field is None: 130 | return default 131 | 132 | return field 133 | 134 | def items(self): 135 | raise NotImplementedError() 136 | -------------------------------------------------------------------------------- /lttnganalyses/core/irq.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from .analysis import Analysis, PeriodData 24 | 25 | 26 | class _PeriodData(PeriodData): 27 | def __init__(self): 28 | # Indexed by irq 'id' (irq or vec) 29 | self.hard_irq_stats = {} 30 | self.softirq_stats = {} 31 | # Log of individual interrupts 32 | self.irq_list = [] 33 | 34 | 35 | class IrqAnalysis(Analysis): 36 | def __init__(self, state, conf): 37 | notification_cbs = { 38 | 'irq_handler_entry': self._process_irq_handler_entry, 39 | 'irq_handler_exit': self._process_irq_handler_exit, 40 | 'softirq_exit': self._process_softirq_exit 41 | } 42 | super().__init__(state, conf, notification_cbs) 43 | 44 | def _create_period_data(self): 45 | return _PeriodData() 46 | 47 | def _process_irq_handler_entry(self, period_data, **kwargs): 48 | id = kwargs['id'] 49 | name = kwargs['irq_name'] 50 | if id not in period_data.hard_irq_stats: 51 | period_data.hard_irq_stats[id] = HardIrqStats(name) 52 | elif name not in period_data.hard_irq_stats[id].names: 53 | period_data.hard_irq_stats[id].names.append(name) 54 | 55 | def _process_irq_handler_exit(self, period_data, **kwargs): 56 | irq = kwargs['hard_irq'] 57 | 58 | if not self._filter_cpu(irq.cpu_id): 59 | return 60 | 61 | if self._conf.min_duration is not None and \ 62 | irq.duration < self._conf.min_duration: 63 | return 64 | if self._conf.max_duration is not None and \ 65 | irq.duration > self._conf.max_duration: 66 | return 67 | 68 | period_data.irq_list.append(irq) 69 | if irq.id not in period_data.hard_irq_stats: 70 | period_data.hard_irq_stats[irq.id] = HardIrqStats() 71 | 72 | period_data.hard_irq_stats[irq.id].update_stats(irq) 73 | 74 | def _process_softirq_exit(self, period_data, **kwargs): 75 | irq = kwargs['softirq'] 76 | 77 | if not self._filter_cpu(irq.cpu_id): 78 | return 79 | 80 | if self._conf.min_duration is not None and \ 81 | irq.duration < self._conf.min_duration: 82 | return 83 | if self._conf.max_duration is not None and \ 84 | irq.duration > self._conf.max_duration: 85 | return 86 | 87 | period_data.irq_list.append(irq) 88 | if irq.id not in period_data.softirq_stats: 89 | name = SoftIrqStats.names[irq.id] 90 | period_data.softirq_stats[irq.id] = SoftIrqStats(name) 91 | 92 | period_data.softirq_stats[irq.id].update_stats(irq) 93 | 94 | 95 | class IrqStats(): 96 | def __init__(self, name): 97 | self._name = name 98 | self.min_duration = None 99 | self.max_duration = None 100 | self.total_duration = 0 101 | self.irq_list = [] 102 | 103 | @property 104 | def name(self): 105 | return self._name 106 | 107 | @property 108 | def count(self): 109 | return len(self.irq_list) 110 | 111 | def update_stats(self, irq): 112 | if self.min_duration is None or irq.duration < self.min_duration: 113 | self.min_duration = irq.duration 114 | 115 | if self.max_duration is None or irq.duration > self.max_duration: 116 | self.max_duration = irq.duration 117 | 118 | self.total_duration += irq.duration 119 | self.irq_list.append(irq) 120 | 121 | def reset(self): 122 | self.min_duration = None 123 | self.max_duration = None 124 | self.total_duration = 0 125 | self.irq_list = [] 126 | 127 | 128 | class HardIrqStats(IrqStats): 129 | NAMES_SEPARATOR = ', ' 130 | 131 | def __init__(self, name='unknown'): 132 | super().__init__(name) 133 | self.names = [name] 134 | 135 | @property 136 | def name(self): 137 | return self.NAMES_SEPARATOR.join(self.names) 138 | 139 | 140 | class SoftIrqStats(IrqStats): 141 | # from include/linux/interrupt.h 142 | names = {0: 'HI_SOFTIRQ', 143 | 1: 'TIMER_SOFTIRQ', 144 | 2: 'NET_TX_SOFTIRQ', 145 | 3: 'NET_RX_SOFTIRQ', 146 | 4: 'BLOCK_SOFTIRQ', 147 | 5: 'BLOCK_IOPOLL_SOFTIRQ', 148 | 6: 'TASKLET_SOFTIRQ', 149 | 7: 'SCHED_SOFTIRQ', 150 | 8: 'HRTIMER_SOFTIRQ', 151 | 9: 'RCU_SOFTIRQ'} 152 | 153 | def __init__(self, name): 154 | super().__init__(name) 155 | self.min_raise_latency = None 156 | self.max_raise_latency = None 157 | self.total_raise_latency = 0 158 | self.raise_count = 0 159 | 160 | def update_stats(self, irq): 161 | super().update_stats(irq) 162 | 163 | if irq.raise_ts is None: 164 | return 165 | 166 | raise_latency = irq.begin_ts - irq.raise_ts 167 | if self.min_raise_latency is None or \ 168 | raise_latency < self.min_raise_latency: 169 | self.min_raise_latency = raise_latency 170 | 171 | if self.max_raise_latency is None or \ 172 | raise_latency > self.max_raise_latency: 173 | self.max_raise_latency = raise_latency 174 | 175 | self.total_raise_latency += raise_latency 176 | self.raise_count += 1 177 | 178 | def reset(self): 179 | super().reset() 180 | self.min_raise_latency = None 181 | self.max_raise_latency = None 182 | self.total_raise_latency = 0 183 | self.raise_count = 0 184 | -------------------------------------------------------------------------------- /lttnganalyses/core/memtop.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from . import stats 24 | from .analysis import Analysis, PeriodData 25 | 26 | 27 | class _PeriodData(PeriodData): 28 | def __init__(self): 29 | self.tids = {} 30 | 31 | 32 | class Memtop(Analysis): 33 | def __init__(self, state, conf): 34 | notification_cbs = { 35 | 'tid_page_alloc': self._process_tid_page_alloc, 36 | 'tid_page_free': self._process_tid_page_free 37 | } 38 | super().__init__(state, conf, notification_cbs) 39 | 40 | def _create_period_data(self): 41 | return _PeriodData() 42 | 43 | def _process_tid_page_alloc(self, period_data, **kwargs): 44 | cpu_id = kwargs['cpu_id'] 45 | proc = kwargs['proc'] 46 | 47 | if not self._filter_process(proc): 48 | return 49 | if not self._filter_cpu(cpu_id): 50 | return 51 | 52 | tid = proc.tid 53 | if tid not in period_data.tids: 54 | period_data.tids[tid] = ProcessMemStats.new_from_process(proc) 55 | 56 | period_data.tids[tid].allocated_pages += 1 57 | 58 | def _process_tid_page_free(self, period_data, **kwargs): 59 | cpu_id = kwargs['cpu_id'] 60 | proc = kwargs['proc'] 61 | 62 | if not self._filter_process(proc): 63 | return 64 | if not self._filter_cpu(cpu_id): 65 | return 66 | 67 | tid = proc.tid 68 | if tid not in period_data.tids: 69 | period_data.tids[tid] = ProcessMemStats.new_from_process(proc) 70 | 71 | period_data.tids[tid].freed_pages += 1 72 | 73 | 74 | class ProcessMemStats(stats.Process): 75 | def __init__(self, pid, tid, comm): 76 | super().__init__(pid, tid, comm) 77 | 78 | self.allocated_pages = 0 79 | self.freed_pages = 0 80 | 81 | def reset(self): 82 | self.allocated_pages = 0 83 | self.freed_pages = 0 84 | -------------------------------------------------------------------------------- /lttnganalyses/core/sched.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import stats 25 | from .analysis import Analysis, PeriodData 26 | 27 | 28 | class _PeriodData(PeriodData): 29 | def __init__(self): 30 | # Log of individual wake scheduling events 31 | self.sched_list = [] 32 | self.min_latency = None 33 | self.max_latency = None 34 | self.total_latency = 0 35 | self.tids = {} 36 | 37 | 38 | class SchedAnalysis(Analysis): 39 | def __init__(self, state, conf): 40 | notification_cbs = { 41 | 'sched_switch_per_tid': self._process_sched_switch, 42 | 'prio_changed': self._process_prio_changed, 43 | } 44 | super().__init__(state, conf, notification_cbs) 45 | 46 | def count(self, period_data): 47 | return len(period_data.sched_list) 48 | 49 | def _create_period_data(self): 50 | return _PeriodData() 51 | 52 | def _process_sched_switch(self, period_data, **kwargs): 53 | cpu_id = kwargs['cpu_id'] 54 | switch_ts = kwargs['timestamp'] 55 | wakee_proc = kwargs['wakee_proc'] 56 | waker_proc = kwargs['waker_proc'] 57 | next_tid = kwargs['next_tid'] 58 | wakeup_ts = wakee_proc.last_wakeup 59 | # print(period_data) 60 | 61 | if not self._filter_process(wakee_proc): 62 | return 63 | if not self._filter_cpu(cpu_id): 64 | return 65 | 66 | if wakeup_ts is None: 67 | return 68 | 69 | latency = switch_ts - wakeup_ts 70 | if self._conf.min_duration is not None and \ 71 | latency < self._conf.min_duration: 72 | return 73 | if self._conf.max_duration is not None and \ 74 | latency > self._conf.max_duration: 75 | return 76 | 77 | if waker_proc is not None and waker_proc.tid not in period_data.tids: 78 | period_data.tids[waker_proc.tid] = \ 79 | ProcessSchedStats.new_from_process(waker_proc) 80 | period_data.tids[waker_proc.tid].update_prio(switch_ts, 81 | waker_proc.prio) 82 | 83 | if next_tid not in period_data.tids: 84 | period_data.tids[next_tid] = \ 85 | ProcessSchedStats.new_from_process(wakee_proc) 86 | period_data.tids[next_tid].update_prio(switch_ts, wakee_proc.prio) 87 | 88 | sched_event = SchedEvent( 89 | wakeup_ts, switch_ts, wakee_proc, waker_proc, cpu_id) 90 | period_data.tids[next_tid].update_stats(sched_event) 91 | self._update_stats(period_data, sched_event) 92 | 93 | def _process_prio_changed(self, period_data, **kwargs): 94 | timestamp = kwargs['timestamp'] 95 | prio = kwargs['prio'] 96 | tid = kwargs['tid'] 97 | 98 | if tid not in period_data.tids: 99 | return 100 | 101 | period_data.tids[tid].update_prio(timestamp, prio) 102 | 103 | def _update_stats(self, period_data, sched_event): 104 | if period_data.min_latency is None or \ 105 | sched_event.latency < period_data.min_latency: 106 | period_data.min_latency = sched_event.latency 107 | 108 | if period_data.max_latency is None or \ 109 | sched_event.latency > period_data.max_latency: 110 | period_data.max_latency = sched_event.latency 111 | 112 | period_data.total_latency += sched_event.latency 113 | period_data.sched_list.append(sched_event) 114 | 115 | 116 | class ProcessSchedStats(stats.Process): 117 | def __init__(self, pid, tid, comm): 118 | super().__init__(pid, tid, comm) 119 | 120 | self.min_latency = None 121 | self.max_latency = None 122 | self.total_latency = 0 123 | self.sched_list = [] 124 | 125 | @property 126 | def count(self): 127 | return len(self.sched_list) 128 | 129 | def update_stats(self, sched_event): 130 | if self.min_latency is None or sched_event.latency < self.min_latency: 131 | self.min_latency = sched_event.latency 132 | 133 | if self.max_latency is None or sched_event.latency > self.max_latency: 134 | self.max_latency = sched_event.latency 135 | 136 | self.total_latency += sched_event.latency 137 | self.sched_list.append(sched_event) 138 | 139 | def reset(self): 140 | super().reset() 141 | self.min_latency = None 142 | self.max_latency = None 143 | self.total_latency = 0 144 | self.sched_list = [] 145 | 146 | 147 | class SchedEvent(): 148 | def __init__(self, wakeup_ts, switch_ts, wakee_proc, waker_proc, 149 | target_cpu): 150 | self.wakeup_ts = wakeup_ts 151 | self.switch_ts = switch_ts 152 | self.wakee_proc = wakee_proc 153 | self.waker_proc = waker_proc 154 | self.prio = wakee_proc.prio 155 | self.target_cpu = target_cpu 156 | self.latency = switch_ts - wakeup_ts 157 | -------------------------------------------------------------------------------- /lttnganalyses/core/stats.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from collections import namedtuple 24 | 25 | 26 | PrioEvent = namedtuple('PrioEvent', ['timestamp', 'prio']) 27 | 28 | 29 | class Stats(): 30 | def reset(self): 31 | raise NotImplementedError() 32 | 33 | 34 | class Process(Stats): 35 | def __init__(self, pid, tid, comm): 36 | self.pid = pid 37 | self.tid = tid 38 | self.comm = comm 39 | self.prio_list = [] 40 | 41 | @classmethod 42 | def new_from_process(cls, proc): 43 | return cls(proc.pid, proc.tid, proc.comm) 44 | 45 | def update_prio(self, timestamp, prio): 46 | self.prio_list.append(PrioEvent(timestamp, prio)) 47 | 48 | def reset(self): 49 | if self.prio_list: 50 | # Keep the last prio as the first for the next period 51 | self.prio_list = self.prio_list[-1:] 52 | 53 | 54 | class IO(Stats): 55 | def __init__(self): 56 | # Number of bytes read or written 57 | self.read = 0 58 | self.write = 0 59 | 60 | def reset(self): 61 | self.read = 0 62 | self.write = 0 63 | 64 | def __iadd__(self, other): 65 | self.read += other.read 66 | self.write += other.write 67 | return self 68 | -------------------------------------------------------------------------------- /lttnganalyses/core/syscalls.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from . import stats 24 | from .analysis import Analysis, PeriodData 25 | 26 | 27 | class _PeriodData(PeriodData): 28 | def __init__(self): 29 | self.tids = {} 30 | self.total_syscalls = 0 31 | 32 | 33 | class SyscallsAnalysis(Analysis): 34 | def __init__(self, state, conf): 35 | notification_cbs = { 36 | 'syscall_exit': self._process_syscall_exit 37 | } 38 | super().__init__(state, conf, notification_cbs) 39 | 40 | def _create_period_data(self): 41 | return _PeriodData() 42 | 43 | def _process_syscall_exit(self, period_data, **kwargs): 44 | cpu_id = kwargs['cpu_id'] 45 | proc = kwargs['proc'] 46 | tid = proc.tid 47 | current_syscall = proc.current_syscall 48 | name = current_syscall.name 49 | 50 | if not self._filter_process(proc): 51 | return 52 | if not self._filter_cpu(cpu_id): 53 | return 54 | 55 | if tid not in period_data.tids: 56 | period_data.tids[tid] = ProcessSyscallStats.new_from_process(proc) 57 | 58 | proc_stats = period_data.tids[tid] 59 | if name not in proc_stats.syscalls: 60 | proc_stats.syscalls[name] = SyscallStats(name) 61 | 62 | proc_stats.syscalls[name].update_stats(current_syscall) 63 | proc_stats.total_syscalls += 1 64 | period_data.total_syscalls += 1 65 | 66 | 67 | class ProcessSyscallStats(stats.Process): 68 | def __init__(self, pid, tid, comm): 69 | super().__init__(pid, tid, comm) 70 | 71 | # indexed by syscall name 72 | self.syscalls = {} 73 | self.total_syscalls = 0 74 | 75 | def reset(self): 76 | pass 77 | 78 | 79 | class SyscallStats(): 80 | def __init__(self, name): 81 | self.name = name 82 | self.min_duration = None 83 | self.max_duration = None 84 | self.total_duration = 0 85 | self.syscalls_list = [] 86 | 87 | @property 88 | def count(self): 89 | return len(self.syscalls_list) 90 | 91 | def update_stats(self, syscall): 92 | duration = syscall.duration 93 | 94 | if self.min_duration is None or self.min_duration > duration: 95 | self.min_duration = duration 96 | if self.max_duration is None or self.max_duration < duration: 97 | self.max_duration = duration 98 | 99 | self.total_duration += duration 100 | self.syscalls_list.append(syscall) 101 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/automaton.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from .sched import SchedStateProvider 25 | from .mem import MemStateProvider 26 | from .irq import IrqStateProvider 27 | from .syscalls import SyscallsStateProvider 28 | from .io import IoStateProvider 29 | from .statedump import StatedumpStateProvider 30 | from .block import BlockStateProvider 31 | from .net import NetStateProvider 32 | from .sv import MemoryManagement 33 | 34 | 35 | class State: 36 | def __init__(self): 37 | self.cpus = {} 38 | self.tids = {} 39 | self.disks = {} 40 | self.mm = MemoryManagement() 41 | self._notification_cbs = {} 42 | # State changes can be handled differently depending on 43 | # version of tracer used, so keep track of it. 44 | self._tracer_version = None 45 | 46 | def register_notification_cbs(self, period_data, cbs): 47 | for name in cbs: 48 | if name not in self._notification_cbs: 49 | self._notification_cbs[name] = [] 50 | # Store the callback in the form of (period_data, function) 51 | self._notification_cbs[name].append((period_data, cbs[name])) 52 | 53 | def send_notification_cb(self, name, **kwargs): 54 | if name in self._notification_cbs: 55 | for cb_tuple in self._notification_cbs[name]: 56 | cb_tuple[1](cb_tuple[0], **kwargs) 57 | 58 | def clear_period_notification_cbs(self, period_data): 59 | for name in self._notification_cbs: 60 | for cb in self._notification_cbs[name]: 61 | if cb[0] == period_data: 62 | self._notification_cbs[name].remove(cb) 63 | 64 | 65 | class Automaton: 66 | def __init__(self): 67 | self._state = State() 68 | self._state_providers = [ 69 | SchedStateProvider(self._state), 70 | MemStateProvider(self._state), 71 | IrqStateProvider(self._state), 72 | SyscallsStateProvider(self._state), 73 | IoStateProvider(self._state), 74 | StatedumpStateProvider(self._state), 75 | BlockStateProvider(self._state), 76 | NetStateProvider(self._state) 77 | ] 78 | 79 | def process_event(self, ev): 80 | for sp in self._state_providers: 81 | sp.process_event(ev) 82 | 83 | @property 84 | def state(self): 85 | return self._state 86 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/block.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import sp, sv 25 | 26 | 27 | class BlockStateProvider(sp.StateProvider): 28 | def __init__(self, state): 29 | cbs = { 30 | 'block_rq_complete': self._process_block_rq_complete, 31 | 'block_rq_issue': self._process_block_rq_issue, 32 | 'block_bio_remap': self._process_block_bio_remap, 33 | 'block_bio_backmerge': self._process_block_bio_backmerge, 34 | } 35 | 36 | super().__init__(state, cbs) 37 | self._remap_requests = [] 38 | 39 | def _process_block_bio_remap(self, event): 40 | dev = event['dev'] 41 | sector = event['sector'] 42 | old_dev = event['old_dev'] 43 | old_sector = event['old_sector'] 44 | 45 | for req in self._remap_requests: 46 | if req.dev == old_dev and req.sector == old_sector: 47 | req.dev = dev 48 | req.sector = sector 49 | return 50 | 51 | req = sv.BlockRemapRequest(dev, sector, old_dev, old_sector) 52 | self._remap_requests.append(req) 53 | 54 | # For backmerge requests, just remove the request from the 55 | # _remap_requests queue, because we rely later on the nr_sector 56 | # which has all the info we need 57 | def _process_block_bio_backmerge(self, event): 58 | dev = event['dev'] 59 | sector = event['sector'] 60 | for remap_req in self._remap_requests: 61 | if remap_req.dev == dev and remap_req.sector == sector: 62 | self._remap_requests.remove(remap_req) 63 | 64 | def _process_block_rq_issue(self, event): 65 | dev = event['dev'] 66 | sector = event['sector'] 67 | nr_sector = event['nr_sector'] 68 | 69 | if nr_sector == 0: 70 | return 71 | 72 | req = sv.BlockIORequest.new_from_rq_issue(event) 73 | 74 | for remap_req in self._remap_requests: 75 | if remap_req.dev == dev and remap_req.sector == sector: 76 | dev = remap_req.old_dev 77 | break 78 | 79 | if dev not in self._state.disks: 80 | self._state.disks[dev] = sv.Disk(dev) 81 | 82 | self._state.disks[dev].pending_requests[sector] = req 83 | 84 | def _process_block_rq_complete(self, event): 85 | dev = event['dev'] 86 | sector = event['sector'] 87 | nr_sector = event['nr_sector'] 88 | 89 | if nr_sector == 0: 90 | return 91 | 92 | for remap_req in self._remap_requests: 93 | if remap_req.dev == dev and remap_req.sector == sector: 94 | dev = remap_req.old_dev 95 | self._remap_requests.remove(remap_req) 96 | break 97 | 98 | if dev not in self._state.disks: 99 | self._state.disks[dev] = sv.Disk(dev) 100 | 101 | disk = self._state.disks[dev] 102 | 103 | # Ignore rq_complete without matching rq_issue 104 | if sector not in disk.pending_requests: 105 | return 106 | 107 | req = disk.pending_requests[sector] 108 | # Ignore rq_complete if nr_sector does not match rq_issue's 109 | if req.nr_sector != nr_sector: 110 | return 111 | 112 | req.update_from_rq_complete(event) 113 | if req.tid in self._state.tids.keys(): 114 | proc = self._state.tids[req.tid] 115 | else: 116 | proc = None 117 | self._state.send_notification_cb('block_rq_complete', req=req, 118 | proc=proc, cpu_id=event['cpu_id'], 119 | disk=disk) 120 | del disk.pending_requests[sector] 121 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/irq.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import sp, sv 25 | 26 | 27 | class IrqStateProvider(sp.StateProvider): 28 | def __init__(self, state): 29 | cbs = { 30 | 'irq_handler_entry': self._process_irq_handler_entry, 31 | 'irq_handler_exit': self._process_irq_handler_exit, 32 | 'softirq_raise': self._process_softirq_raise, 33 | 'softirq_entry': self._process_softirq_entry, 34 | 'softirq_exit': self._process_softirq_exit 35 | } 36 | 37 | super().__init__(state, cbs) 38 | 39 | def _get_cpu(self, cpu_id): 40 | if cpu_id not in self._state.cpus: 41 | self._state.cpus[cpu_id] = sv.CPU(cpu_id) 42 | 43 | return self._state.cpus[cpu_id] 44 | 45 | # Hard IRQs 46 | def _process_irq_handler_entry(self, event): 47 | cpu = self._get_cpu(event['cpu_id']) 48 | irq = sv.HardIRQ.new_from_irq_handler_entry(event) 49 | cpu.current_hard_irq = irq 50 | 51 | self._state.send_notification_cb('irq_handler_entry', 52 | id=irq.id, 53 | irq_name=event['name']) 54 | 55 | def _process_irq_handler_exit(self, event): 56 | cpu = self._get_cpu(event['cpu_id']) 57 | if cpu.current_hard_irq is None or \ 58 | cpu.current_hard_irq.id != event['irq']: 59 | cpu.current_hard_irq = None 60 | return 61 | 62 | cpu.current_hard_irq.end_ts = event.timestamp 63 | cpu.current_hard_irq.ret = event['ret'] 64 | 65 | self._state.send_notification_cb('irq_handler_exit', 66 | hard_irq=cpu.current_hard_irq) 67 | cpu.current_hard_irq = None 68 | 69 | # SoftIRQs 70 | def _process_softirq_raise(self, event): 71 | cpu = self._get_cpu(event['cpu_id']) 72 | vec = event['vec'] 73 | 74 | if vec not in cpu.current_softirqs: 75 | cpu.current_softirqs[vec] = [] 76 | 77 | # Don't append a SoftIRQ object if one has already been raised, 78 | # because they are level-triggered. The only exception to this 79 | # is if the first SoftIRQ object already had a begin_ts which 80 | # means this raise was triggered after its entry, and will be 81 | # handled in the following softirq_entry 82 | if cpu.current_softirqs[vec] and \ 83 | cpu.current_softirqs[vec][0].begin_ts is None: 84 | return 85 | 86 | irq = sv.SoftIRQ.new_from_softirq_raise(event) 87 | cpu.current_softirqs[vec].append(irq) 88 | 89 | def _process_softirq_entry(self, event): 90 | cpu = self._get_cpu(event['cpu_id']) 91 | vec = event['vec'] 92 | 93 | if vec not in cpu.current_softirqs: 94 | cpu.current_softirqs[vec] = [] 95 | 96 | if cpu.current_softirqs[vec]: 97 | cpu.current_softirqs[vec][0].begin_ts = event.timestamp 98 | else: 99 | # SoftIRQ entry without a corresponding raise 100 | irq = sv.SoftIRQ.new_from_softirq_entry(event) 101 | cpu.current_softirqs[vec].append(irq) 102 | 103 | def _process_softirq_exit(self, event): 104 | cpu = self._get_cpu(event['cpu_id']) 105 | vec = event['vec'] 106 | # List of enqueued softirqs for the current cpu/vec 107 | # combination. None if vec is not found in the dictionary. 108 | current_softirqs = cpu.current_softirqs.get(vec) 109 | 110 | # Ignore the exit if either vec was not in the cpu's dict or 111 | # if its irq list was empty (i.e. no matching raise). 112 | if not current_softirqs: 113 | return 114 | 115 | current_softirqs[0].end_ts = event.timestamp 116 | self._state.send_notification_cb('softirq_exit', 117 | softirq=current_softirqs[0]) 118 | del current_softirqs[0] 119 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/mem.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import sp 25 | 26 | 27 | class MemStateProvider(sp.StateProvider): 28 | def __init__(self, state): 29 | cbs = { 30 | 'mm_page_alloc': self._process_mm_page_alloc, 31 | 'kmem_mm_page_alloc': self._process_mm_page_alloc, 32 | 'mm_page_free': self._process_mm_page_free, 33 | 'kmem_mm_page_free': self._process_mm_page_free, 34 | } 35 | 36 | super().__init__(state, cbs) 37 | 38 | def _get_current_proc(self, event): 39 | cpu_id = event['cpu_id'] 40 | if cpu_id not in self._state.cpus: 41 | return None 42 | 43 | cpu = self._state.cpus[cpu_id] 44 | if cpu.current_tid is None: 45 | return None 46 | 47 | return self._state.tids[cpu.current_tid] 48 | 49 | def _process_mm_page_alloc(self, event): 50 | self._state.mm.page_count += 1 51 | 52 | # Increment the number of pages allocated during the execution 53 | # of all currently syscall io requests 54 | for process in self._state.tids.values(): 55 | if process.current_syscall is None: 56 | continue 57 | 58 | if process.current_syscall.io_rq: 59 | process.current_syscall.io_rq.pages_allocated += 1 60 | 61 | current_process = self._get_current_proc(event) 62 | if current_process is None: 63 | return 64 | 65 | self._state.send_notification_cb('tid_page_alloc', 66 | proc=current_process, 67 | cpu_id=event['cpu_id']) 68 | 69 | def _process_mm_page_free(self, event): 70 | if self._state.mm.page_count == 0: 71 | return 72 | 73 | self._state.mm.page_count -= 1 74 | 75 | current_process = self._get_current_proc(event) 76 | if current_process is None: 77 | return 78 | 79 | self._state.send_notification_cb('tid_page_free', 80 | proc=current_process, 81 | cpu_id=event['cpu_id']) 82 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/net.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import sp, sv 25 | 26 | 27 | class NetStateProvider(sp.StateProvider): 28 | def __init__(self, state): 29 | cbs = { 30 | 'net_dev_xmit': self._process_net_dev_xmit, 31 | 'netif_receive_skb': self._process_netif_receive_skb, 32 | } 33 | 34 | super().__init__(state, cbs) 35 | 36 | def _process_net_dev_xmit(self, event): 37 | self._state.send_notification_cb('net_dev_xmit', 38 | iface_name=event['name'], 39 | sent_bytes=event['len'], 40 | cpu_id=event['cpu_id']) 41 | 42 | cpu_id = event['cpu_id'] 43 | if cpu_id not in self._state.cpus: 44 | return 45 | 46 | cpu = self._state.cpus[cpu_id] 47 | if cpu.current_tid is None: 48 | return 49 | 50 | proc = self._state.tids[cpu.current_tid] 51 | current_syscall = proc.current_syscall 52 | if current_syscall is None: 53 | return 54 | 55 | if proc.pid is not None and proc.pid != proc.tid: 56 | proc = self._state.tids[proc.pid] 57 | 58 | if current_syscall.name in sv.SyscallConsts.WRITE_SYSCALLS: 59 | # TODO: find a way to set fd_type on the write rq to allow 60 | # setting FD Type if FD hasn't yet been created 61 | fd = current_syscall.io_rq.fd 62 | if fd in proc.fds and proc.fds[fd].fd_type == sv.FDType.unknown: 63 | proc.fds[fd].fd_type = sv.FDType.maybe_net 64 | 65 | def _process_netif_receive_skb(self, event): 66 | self._state.send_notification_cb('netif_receive_skb', 67 | iface_name=event['name'], 68 | recv_bytes=event['len'], 69 | cpu_id=event['cpu_id']) 70 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/sp.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | 24 | class StateProvider: 25 | def __init__(self, state, cbs): 26 | self._state = state 27 | self._cbs = cbs 28 | 29 | def process_event(self, ev): 30 | name = ev.name 31 | 32 | if name in self._cbs: 33 | self._cbs[name](ev) 34 | # for now we process all the syscalls at the same place 35 | elif 'syscall_entry' in self._cbs and \ 36 | (name.startswith('sys_') or name.startswith('syscall_entry_')): 37 | self._cbs['syscall_entry'](ev) 38 | elif 'syscall_exit' in self._cbs and \ 39 | (name.startswith('exit_syscall') or 40 | name.startswith('syscall_exit_')): 41 | self._cbs['syscall_exit'](ev) 42 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/statedump.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | import os 25 | from . import sp, sv 26 | 27 | 28 | class StatedumpStateProvider(sp.StateProvider): 29 | def __init__(self, state): 30 | cbs = { 31 | 'lttng_statedump_process_state': 32 | self._process_lttng_statedump_process_state, 33 | 'lttng_statedump_file_descriptor': 34 | self._process_lttng_statedump_file_descriptor, 35 | 'lttng_statedump_block_device': 36 | self._process_lttng_statedump_block_device 37 | } 38 | 39 | super().__init__(state, cbs) 40 | 41 | def _process_lttng_statedump_block_device(self, event): 42 | dev = event['dev'] 43 | diskname = event['diskname'] 44 | 45 | if dev not in self._state.disks: 46 | self._state.disks[dev] = sv.Disk(dev, diskname=diskname) 47 | elif self._state.disks[dev].diskname is None: 48 | self._state.disks[dev].diskname = diskname 49 | self._state.send_notification_cb('lttng_statedump_block_device', 50 | dev=dev, diskname=diskname) 51 | 52 | def _process_lttng_statedump_process_state(self, event): 53 | tid = event['tid'] 54 | pid = event['pid'] 55 | name = event['name'] 56 | # prio is not in the payload for LTTng-modules < 2.8. Using 57 | # get() will set it to None if the key is not found 58 | prio = event.get('prio') 59 | 60 | if tid not in self._state.tids: 61 | self._state.tids[tid] = sv.Process(tid=tid) 62 | 63 | proc = self._state.tids[tid] 64 | # Even if the process got created earlier, some info might be 65 | # missing, add it now. 66 | proc.pid = pid 67 | proc.comm = name 68 | # However don't override the prio value if we already got the 69 | # information from sched_* events. 70 | if proc.prio is None: 71 | proc.prio = prio 72 | 73 | if pid != tid: 74 | # create the parent 75 | if pid not in self._state.tids: 76 | # FIXME: why is the parent's name set to that of the 77 | # child? does that make sense? 78 | 79 | # tid == pid for the parent process 80 | self._state.tids[pid] = sv.Process(tid=pid, pid=pid, comm=name) 81 | 82 | parent = self._state.tids[pid] 83 | # If the thread had opened FDs, they need to be assigned 84 | # to the parent. 85 | StatedumpStateProvider._assign_fds_to_parent(proc, parent) 86 | self._state.send_notification_cb('create_parent_proc', 87 | proc=proc, 88 | parent_proc=parent) 89 | 90 | def _process_lttng_statedump_file_descriptor(self, event): 91 | pid = event['pid'] 92 | fd = event['fd'] 93 | filename = event['filename'] 94 | cloexec = event['flags'] & os.O_CLOEXEC == os.O_CLOEXEC 95 | 96 | if pid not in self._state.tids: 97 | self._state.tids[pid] = sv.Process(tid=pid, pid=pid) 98 | 99 | proc = self._state.tids[pid] 100 | 101 | if fd not in proc.fds: 102 | proc.fds[fd] = sv.FD(fd, filename, sv.FDType.unknown, cloexec) 103 | self._state.send_notification_cb('create_fd', 104 | fd=fd, 105 | parent_proc=proc, 106 | timestamp=event.timestamp, 107 | cpu_id=event['cpu_id']) 108 | else: 109 | # just fix the filename 110 | proc.fds[fd].filename = filename 111 | self._state.send_notification_cb('update_fd', 112 | fd=fd, 113 | parent_proc=proc, 114 | timestamp=event.timestamp, 115 | cpu_id=event['cpu_id']) 116 | 117 | @staticmethod 118 | def _assign_fds_to_parent(proc, parent): 119 | if proc.fds: 120 | toremove = [] 121 | for fd in proc.fds: 122 | if fd not in parent.fds: 123 | parent.fds[fd] = proc.fds[fd] 124 | else: 125 | # best effort to fix the filename 126 | if not parent.fds[fd].filename: 127 | parent.fds[fd].filename = proc.fds[fd].filename 128 | toremove.append(fd) 129 | for fd in toremove: 130 | del proc.fds[fd] 131 | -------------------------------------------------------------------------------- /lttnganalyses/linuxautomaton/syscalls.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2015 - Julien Desfossez 4 | # 2015 - Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from . import sp, sv 25 | 26 | 27 | class SyscallsStateProvider(sp.StateProvider): 28 | def __init__(self, state): 29 | cbs = { 30 | 'syscall_entry': self._process_syscall_entry, 31 | 'syscall_exit': self._process_syscall_exit 32 | } 33 | 34 | super().__init__(state, cbs) 35 | 36 | def _process_syscall_entry(self, event): 37 | cpu_id = event['cpu_id'] 38 | 39 | if cpu_id not in self._state.cpus: 40 | return 41 | 42 | cpu = self._state.cpus[cpu_id] 43 | if cpu.current_tid is None: 44 | return 45 | 46 | proc = self._state.tids[cpu.current_tid] 47 | proc.current_syscall = sv.SyscallEvent.new_from_entry(event) 48 | 49 | def _process_syscall_exit(self, event): 50 | cpu_id = event['cpu_id'] 51 | if cpu_id not in self._state.cpus: 52 | return 53 | 54 | cpu = self._state.cpus[cpu_id] 55 | if cpu.current_tid is None: 56 | return 57 | 58 | proc = self._state.tids[cpu.current_tid] 59 | current_syscall = proc.current_syscall 60 | if current_syscall is None: 61 | return 62 | 63 | current_syscall.process_exit(event) 64 | 65 | self._state.send_notification_cb('syscall_exit', 66 | proc=proc, 67 | event=event, 68 | cpu_id=cpu_id) 69 | 70 | # If it's an IO Syscall, the IO state provider will take care of 71 | # clearing the current syscall, so only clear here if it's not 72 | if current_syscall.name not in sv.SyscallConsts.IO_SYSCALLS: 73 | self._state.tids[cpu.current_tid].current_syscall = None 74 | -------------------------------------------------------------------------------- /mit-license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 EfficiOS Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /parser_generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2015 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import sys 26 | import os 27 | import stat 28 | import argparse 29 | 30 | try: 31 | from babeltrace import TraceCollection, CTFScope 32 | except ImportError: 33 | # quick fix for debian-based distros 34 | sys.path.append("/usr/local/lib/python%d.%d/site-packages" % 35 | (sys.version_info.major, sys.version_info.minor)) 36 | from babeltrace import TraceCollection, CTFScope 37 | 38 | preambule = """#!/usr/bin/env python3 39 | 40 | import sys 41 | import time 42 | import argparse 43 | 44 | NSEC_PER_SEC = 1000000000 45 | 46 | try: 47 | from babeltrace import TraceCollection 48 | except ImportError: 49 | # quick fix for debian-based distros 50 | sys.path.append("/usr/local/lib/python%d.%d/site-packages" % 51 | (sys.version_info.major, sys.version_info.minor)) 52 | from babeltrace import TraceCollection 53 | 54 | 55 | class TraceParser: 56 | def __init__(self, trace): 57 | self.trace = trace 58 | self.event_count = {} 59 | 60 | def ns_to_hour_nsec(self, ns): 61 | d = time.localtime(ns/NSEC_PER_SEC) 62 | return "%02d:%02d:%02d.%09d" % (d.tm_hour, d.tm_min, d.tm_sec, 63 | ns % NSEC_PER_SEC) 64 | 65 | def parse(self): 66 | # iterate over all the events 67 | for event in self.trace.events: 68 | if not event.name in self.event_count.keys(): 69 | self.event_count[event.name] = 0 70 | method_name = "handle_%s" % \ 71 | event.name.replace(":", "_").replace("+", "_") 72 | # call the function to handle each event individually 73 | if hasattr(TraceParser, method_name): 74 | func = getattr(TraceParser, method_name) 75 | func(self, event) 76 | # print statistics after parsing the trace 77 | print("Total event count:") 78 | for e in self.event_count.keys(): 79 | print("- %s: %d" % (e, self.event_count[e])) 80 | 81 | """ 82 | 83 | end = """ 84 | if __name__ == "__main__": 85 | parser = argparse.ArgumentParser(description='Trace parser') 86 | parser.add_argument('path', metavar="", help='Trace path') 87 | args = parser.parse_args() 88 | 89 | traces = TraceCollection() 90 | handle = traces.add_traces_recursive(args.path, "ctf") 91 | if handle is None: 92 | sys.exit(1) 93 | 94 | t = TraceParser(traces) 95 | t.parse() 96 | 97 | for h in handle.values(): 98 | traces.remove_trace(h) 99 | """ 100 | 101 | 102 | def gen_parser(handle, fd, args): 103 | for h in handle.values(): 104 | for event in h.events: 105 | fmt_str = "[%s] %s: { cpu_id = %s }, { " 106 | fmt_fields = "self.ns_to_hour_nsec(timestamp), event.name, " \ 107 | "cpu_id, " 108 | name = event.name.replace(":", "_").replace("+", "_") 109 | fd.write(" def handle_%s(self, event):\n" % (name)) 110 | fd.write(" timestamp = event.timestamp\n") 111 | fd.write(" cpu_id = event[\"cpu_id\"]\n") 112 | for field in event.fields: 113 | if field.scope == CTFScope.EVENT_FIELDS: 114 | fname = field.name 115 | # some field names are reserved keywords/variables 116 | if fname == "in": 117 | fname = "_in" 118 | if fname == "event": 119 | fname = "_event" 120 | if fname == "from": 121 | fname = "_from" 122 | fd.write(" %s = event[\"%s\"]\n" % (fname, 123 | field.name)) 124 | fmt_str = fmt_str + field.name + " = %s, " 125 | fmt_fields = fmt_fields + "%s, " % (fname) 126 | fd.write("\n self.event_count[event.name] += 1\n") 127 | if not args.quiet: 128 | fd.write(" print(\"%s }\" %% (%s))\n\n" % 129 | (fmt_str[0:-2], fmt_fields[0:-1])) 130 | 131 | 132 | if __name__ == "__main__": 133 | parser = argparse.ArgumentParser(description='Trace parser generator') 134 | parser.add_argument('path', metavar="", help='Trace path') 135 | parser.add_argument('-o', '--output', type=str, 136 | metavar="", 137 | help='Output script name') 138 | parser.add_argument('-q', '--quiet', action="store_true", 139 | help='Generate a quiet parser (no print)') 140 | args = parser.parse_args() 141 | 142 | traces = TraceCollection() 143 | handle = traces.add_traces_recursive(args.path, "ctf") 144 | if handle is None: 145 | sys.exit(1) 146 | 147 | if not args.output: 148 | output = "generated-parser.py" 149 | else: 150 | output = args.output 151 | 152 | fd = open(output, "w") 153 | fd.write(preambule) 154 | gen_parser(handle, fd, args) 155 | 156 | for h in handle.values(): 157 | traces.remove_trace(h) 158 | fd.write(end) 159 | fd.close() 160 | os.chmod(output, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | 161 | stat.S_IRGRP | stat.S_IXGRP | 162 | stat.S_IROTH | stat.S_IXOTH) 163 | print("A trace parser for this trace has been written in", output) 164 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyparsing 2 | progressbar33 [progressbar] 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [versioneer] 2 | VCS = git 3 | style = pep440 4 | versionfile_source = lttnganalyses/_version.py 5 | versionfile_build = lttnganalyses/_version.py 6 | tag_prefix = v 7 | parentdir_prefix = lttnganalyses- 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (C) 2015 - Michael Jeanson 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """LTTnganalyses setup script""" 24 | 25 | 26 | import shutil 27 | import sys 28 | from setuptools import setup 29 | import versioneer 30 | 31 | if sys.version_info[0:2] < (3, 4): 32 | raise RuntimeError("Python version >= 3.4 required.") 33 | 34 | if 'install' in sys.argv: 35 | if shutil.which('babeltrace') is None: 36 | print('lttnganalysescli needs the babeltrace executable.\n' 37 | 'See https://www.efficios.com/babeltrace for more info.', 38 | file=sys.stderr) 39 | sys.exit(1) 40 | 41 | try: 42 | __import__('babeltrace') 43 | except ImportError: 44 | print('lttnganalysescli needs the babeltrace python bindings.\n' 45 | 'See https://www.efficios.com/babeltrace for more info.', 46 | file=sys.stderr) 47 | sys.exit(1) 48 | 49 | 50 | def read_file(filename): 51 | """Read all contents of ``filename``.""" 52 | with open(filename, encoding='utf-8') as source: 53 | return source.read() 54 | 55 | 56 | setup( 57 | name='lttnganalyses', 58 | version=versioneer.get_version(), 59 | cmdclass=versioneer.get_cmdclass(), 60 | 61 | description='LTTng analyses', 62 | long_description=read_file('README.rst'), 63 | 64 | url='https://github.com/lttng/lttng-analyses', 65 | 66 | author='Julien Desfossez', 67 | author_email='jdesfossez@efficios.com', 68 | 69 | license='MIT', 70 | 71 | classifiers=[ 72 | 'Development Status :: 4 - Beta', 73 | 74 | 'Intended Audience :: Developers', 75 | 'Intended Audience :: System Administrators', 76 | 'Topic :: System :: Monitoring', 77 | 78 | 'License :: OSI Approved :: MIT License', 79 | 80 | 'Programming Language :: Python :: 3.4', 81 | ], 82 | 83 | keywords='lttng tracing', 84 | 85 | packages=[ 86 | 'lttnganalyses', 87 | 'lttnganalyses.common', 88 | 'lttnganalyses.core', 89 | 'lttnganalyses.cli', 90 | 'lttnganalyses.linuxautomaton' 91 | ], 92 | 93 | entry_points={ 94 | 'console_scripts': [ 95 | # human-readable output 96 | 'lttng-cputop = lttnganalyses.cli.cputop:run', 97 | 'lttng-iolatencyfreq = lttnganalyses.cli.io:runfreq', 98 | 'lttng-iolatencystats = lttnganalyses.cli.io:runstats', 99 | 'lttng-iolatencytop = lttnganalyses.cli.io:runlatencytop', 100 | 'lttng-iolog = lttnganalyses.cli.io:runlog', 101 | 'lttng-iousagetop = lttnganalyses.cli.io:runusage', 102 | 'lttng-irqfreq = lttnganalyses.cli.irq:runfreq', 103 | 'lttng-irqlog = lttnganalyses.cli.irq:runlog', 104 | 'lttng-irqstats = lttnganalyses.cli.irq:runstats', 105 | 'lttng-memtop = lttnganalyses.cli.memtop:run', 106 | 'lttng-syscallstats = lttnganalyses.cli.syscallstats:run', 107 | 'lttng-schedlog = lttnganalyses.cli.sched:runlog', 108 | 'lttng-schedtop = lttnganalyses.cli.sched:runtop', 109 | 'lttng-schedstats = lttnganalyses.cli.sched:runstats', 110 | 'lttng-schedfreq = lttnganalyses.cli.sched:runfreq', 111 | 'lttng-periodlog = lttnganalyses.cli.periods:runlog', 112 | 'lttng-periodtop = lttnganalyses.cli.periods:runtop', 113 | 'lttng-periodstats = lttnganalyses.cli.periods:runstats', 114 | 'lttng-periodfreq = lttnganalyses.cli.periods:runfreq', 115 | 116 | # MI mode 117 | 'lttng-cputop-mi = lttnganalyses.cli.cputop:run_mi', 118 | 'lttng-memtop-mi = lttnganalyses.cli.memtop:run_mi', 119 | 'lttng-syscallstats-mi = lttnganalyses.cli.syscallstats:run_mi', 120 | 'lttng-irqfreq-mi = lttnganalyses.cli.irq:runfreq_mi', 121 | 'lttng-irqlog-mi = lttnganalyses.cli.irq:runlog_mi', 122 | 'lttng-irqstats-mi = lttnganalyses.cli.irq:runstats_mi', 123 | 'lttng-iolatencyfreq-mi = lttnganalyses.cli.io:runfreq_mi', 124 | 'lttng-iolatencystats-mi = lttnganalyses.cli.io:runstats_mi', 125 | 'lttng-iolatencytop-mi = lttnganalyses.cli.io:runlatencytop_mi', 126 | 'lttng-iolog-mi = lttnganalyses.cli.io:runlog_mi', 127 | 'lttng-iousagetop-mi = lttnganalyses.cli.io:runusage_mi', 128 | 'lttng-schedlog-mi = lttnganalyses.cli.sched:runlog_mi', 129 | 'lttng-schedtop-mi = lttnganalyses.cli.sched:runtop_mi', 130 | 'lttng-schedstats-mi = lttnganalyses.cli.sched:runstats_mi', 131 | 'lttng-schedfreq-mi = lttnganalyses.cli.sched:runfreq_mi', 132 | 'lttng-periodlog-mi = lttnganalyses.cli.periods:runlog_mi', 133 | 'lttng-periodtop-mi = lttnganalyses.cli.periods:runtop_mi', 134 | 'lttng-periodstats-mi = lttnganalyses.cli.periods:runstats_mi', 135 | 'lttng-periodfreq-mi = lttnganalyses.cli.periods:runfreq_mi', 136 | ], 137 | }, 138 | 139 | scripts=[ 140 | 'lttng-analyses-record', 141 | 'lttng-track-process' 142 | ], 143 | 144 | install_requires=[ 145 | 'pyparsing', 146 | ], 147 | 148 | extras_require={ 149 | 'progressbar': ["progressbar"] 150 | }, 151 | 152 | test_suite='tests', 153 | ) 154 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-cov 3 | flake8>=2.5.0 4 | coverage>=4.1 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from . import common 24 | from . import integration 25 | -------------------------------------------------------------------------------- /tests/common/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/common/test_format_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import unittest 24 | from lttnganalyses.core import stats 25 | from lttnganalyses.common import format_utils 26 | from .utils import TimezoneUtils 27 | 28 | 29 | class TestFormatSize(unittest.TestCase): 30 | def test_negative(self): 31 | self.assertRaises(ValueError, format_utils.format_size, -1) 32 | 33 | def test_zero(self): 34 | result = format_utils.format_size(0) 35 | result_decimal = format_utils.format_size(0, binary_prefix=False) 36 | 37 | self.assertEqual(result, '0 B') 38 | self.assertEqual(result_decimal, '0 B') 39 | 40 | def test_huge(self): 41 | # 2000 YiB or 2475.88 YB 42 | huge_value = 2417851639229258349412352000 43 | result = format_utils.format_size(huge_value) 44 | result_decimal = format_utils.format_size(huge_value, 45 | binary_prefix=False) 46 | 47 | self.assertEqual(result, '2000.00 YiB') 48 | self.assertEqual(result_decimal, '2417.85 YB') 49 | 50 | def test_reasonable(self): 51 | # 2 GB or 1.86 GiB 52 | reasonable_value = 2000000000 53 | result = format_utils.format_size(reasonable_value) 54 | result_decimal = format_utils.format_size(reasonable_value, 55 | binary_prefix=False) 56 | 57 | self.assertEqual(result, '1.86 GiB') 58 | self.assertEqual(result_decimal, '2.00 GB') 59 | 60 | 61 | class TestFormatPrioList(unittest.TestCase): 62 | def test_empty(self): 63 | prio_list = [] 64 | result = format_utils.format_prio_list(prio_list) 65 | 66 | self.assertEqual(result, '[]') 67 | 68 | def test_one_prio(self): 69 | prio_list = [stats.PrioEvent(0, 0)] 70 | result = format_utils.format_prio_list(prio_list) 71 | 72 | self.assertEqual(result, '[0]') 73 | 74 | def test_multiple_prios(self): 75 | prio_list = [stats.PrioEvent(0, 0), stats.PrioEvent(0, 1)] 76 | result = format_utils.format_prio_list(prio_list) 77 | 78 | self.assertEqual(result, '[0, 1]') 79 | 80 | def test_repeated_prio(self): 81 | prio_list = [stats.PrioEvent(0, 0), stats.PrioEvent(0, 0)] 82 | result = format_utils.format_prio_list(prio_list) 83 | 84 | self.assertEqual(result, '[0 (2 times)]') 85 | 86 | def test_repeated_prios(self): 87 | prio_list = [ 88 | stats.PrioEvent(0, 0), stats.PrioEvent(0, 1), 89 | stats.PrioEvent(0, 0), stats.PrioEvent(0, 1) 90 | ] 91 | result = format_utils.format_prio_list(prio_list) 92 | 93 | self.assertEqual(result, '[0 (2 times), 1 (2 times)]') 94 | 95 | 96 | class TestFormatTimestamp(unittest.TestCase): 97 | # This may or may not be the time of the Linux 0.0.1 announcement. 98 | ARBITRARY_TIMESTAMP = 683153828123456789 99 | 100 | def setUp(self): 101 | self.tz_utils = TimezoneUtils() 102 | self.tz_utils.set_up_timezone() 103 | 104 | def tearDown(self): 105 | self.tz_utils.tear_down_timezone() 106 | 107 | def test_time(self): 108 | result = format_utils.format_timestamp(self.ARBITRARY_TIMESTAMP) 109 | result_gmt = format_utils.format_timestamp( 110 | self.ARBITRARY_TIMESTAMP, gmt=True 111 | ) 112 | 113 | self.assertEqual(result, '16:57:08.123456789') 114 | self.assertEqual(result_gmt, '20:57:08.123456789') 115 | 116 | def test_date(self): 117 | result = format_utils.format_timestamp( 118 | self.ARBITRARY_TIMESTAMP, print_date=True 119 | ) 120 | result_gmt = format_utils.format_timestamp( 121 | self.ARBITRARY_TIMESTAMP, print_date=True, gmt=True 122 | ) 123 | 124 | self.assertEqual(result, '1991-08-25 16:57:08.123456789') 125 | self.assertEqual(result_gmt, '1991-08-25 20:57:08.123456789') 126 | 127 | def test_negative(self): 128 | # Make sure the time module handles pre-epoch dates correctly 129 | result = format_utils.format_timestamp( 130 | -self.ARBITRARY_TIMESTAMP, print_date=True 131 | ) 132 | result_gmt = format_utils.format_timestamp( 133 | -self.ARBITRARY_TIMESTAMP, print_date=True, gmt=True 134 | ) 135 | 136 | self.assertEqual(result, '1948-05-08 23:02:51.876543211') 137 | self.assertEqual(result_gmt, '1948-05-09 03:02:51.876543211') 138 | 139 | 140 | class TestFormatTimeRange(unittest.TestCase): 141 | BEGIN_TS = 683153828123456789 142 | # 1 hour later 143 | END_TS = 683157428123456789 144 | 145 | def _mock_format_timestamp(self, timestamp, print_date, gmt): 146 | date_str = '1991-08-25 ' 147 | 148 | if timestamp == TestFormatTimeRange.BEGIN_TS: 149 | if gmt: 150 | time_str = '20:57:08.123456789' 151 | else: 152 | time_str = '16:57:08.123456789' 153 | elif timestamp == TestFormatTimeRange.END_TS: 154 | if gmt: 155 | time_str = '21:57:08.123456789' 156 | else: 157 | time_str = '17:57:08.123456789' 158 | 159 | if print_date: 160 | return date_str + time_str 161 | else: 162 | return time_str 163 | 164 | def setUp(self): 165 | self._original_format_timestamp = format_utils.format_timestamp 166 | format_utils.format_timestamp = self._mock_format_timestamp 167 | 168 | def tearDown(self): 169 | format_utils.format_timestamp = self._original_format_timestamp 170 | 171 | def test_time_only(self): 172 | result = format_utils.format_time_range( 173 | self.BEGIN_TS, self.END_TS 174 | ) 175 | result_gmt = format_utils.format_time_range( 176 | self.BEGIN_TS, self.END_TS, gmt=True 177 | ) 178 | 179 | self.assertEqual(result, 180 | '[16:57:08.123456789, 17:57:08.123456789]') 181 | self.assertEqual(result_gmt, 182 | '[20:57:08.123456789, 21:57:08.123456789]') 183 | 184 | def test_print_date(self): 185 | result = format_utils.format_time_range( 186 | self.BEGIN_TS, self.END_TS, print_date=True 187 | ) 188 | result_gmt = format_utils.format_time_range( 189 | self.BEGIN_TS, self.END_TS, print_date=True, gmt=True 190 | ) 191 | 192 | self.assertEqual( 193 | result, 194 | '[1991-08-25 16:57:08.123456789, 1991-08-25 17:57:08.123456789]' 195 | ) 196 | self.assertEqual( 197 | result_gmt, 198 | '[1991-08-25 20:57:08.123456789, 1991-08-25 21:57:08.123456789]' 199 | ) 200 | 201 | 202 | class TestFormatIpv4(unittest.TestCase): 203 | IP_INTEGER = 0x7f000001 204 | IP_SEQUENCE = [127, 0, 0, 1] 205 | 206 | def test_integer(self): 207 | result = format_utils.format_ipv4(self.IP_INTEGER) 208 | 209 | self.assertEqual(result, '127.0.0.1') 210 | 211 | def test_sequence(self): 212 | result = format_utils.format_ipv4(self.IP_SEQUENCE) 213 | 214 | self.assertEqual(result, '127.0.0.1') 215 | 216 | def test_with_port(self): 217 | result = format_utils.format_ipv4(self.IP_SEQUENCE, port=8080) 218 | 219 | self.assertEqual(result, '127.0.0.1:8080') 220 | -------------------------------------------------------------------------------- /tests/common/test_trace_utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import unittest 24 | from datetime import date 25 | from lttnganalyses.common import trace_utils 26 | from .utils import TimezoneUtils 27 | 28 | 29 | # Mock of babeltrace's TraceCollection, used to test date methods 30 | class TraceCollection(): 31 | def __init__(self, begin_ts, end_ts): 32 | self.begin_ts = begin_ts 33 | self.end_ts = end_ts 34 | 35 | @property 36 | def timestamp_begin(self): 37 | return self.begin_ts 38 | 39 | @property 40 | def timestamp_end(self): 41 | return self.end_ts 42 | 43 | 44 | class TestIsMultiDayTraceCollection(unittest.TestCase): 45 | def setUp(self): 46 | self.tz_utils = TimezoneUtils() 47 | self.tz_utils.set_up_timezone() 48 | 49 | def tearDown(self): 50 | self.tz_utils.tear_down_timezone() 51 | 52 | def test_same_day(self): 53 | begin_ts = 683153828123456789 54 | # 1 hour later 55 | end_ts = 683157428123456789 56 | collection = TraceCollection(begin_ts, end_ts) 57 | result = trace_utils.is_multi_day_trace_collection(collection) 58 | 59 | self.assertFalse(result) 60 | 61 | def test_different_day(self): 62 | begin_ts = 683153828123456789 63 | # 24 hours later 64 | end_ts = 683240228123456789 65 | collection = TraceCollection(begin_ts, end_ts) 66 | result = trace_utils.is_multi_day_trace_collection(collection) 67 | 68 | self.assertTrue(result) 69 | 70 | 71 | class TestGetTraceCollectionDate(unittest.TestCase): 72 | def setUp(self): 73 | self.tz_utils = TimezoneUtils() 74 | self.tz_utils.set_up_timezone() 75 | 76 | def tearDown(self): 77 | self.tz_utils.tear_down_timezone() 78 | 79 | def test_single_day(self): 80 | begin_ts = 683153828123456789 81 | # 1 hour later 82 | end_ts = 683157428123456789 83 | collection = TraceCollection(begin_ts, end_ts) 84 | result = trace_utils.get_trace_collection_date(collection) 85 | expected = date(1991, 8, 25) 86 | 87 | self.assertEqual(result, expected) 88 | 89 | def test_multi_day(self): 90 | begin_ts = 683153828123456789 91 | # 24 hours later 92 | end_ts = 683240228123456789 93 | collection = TraceCollection(begin_ts, end_ts) 94 | 95 | self.assertRaises(ValueError, trace_utils.get_trace_collection_date, 96 | collection) 97 | 98 | 99 | class TestGetSyscallName(unittest.TestCase): 100 | class Event(): 101 | def __init__(self, name): 102 | self.name = name 103 | 104 | def test_sys(self): 105 | event = self.Event('sys_open') 106 | result = trace_utils.get_syscall_name(event) 107 | 108 | self.assertEqual(result, 'open') 109 | 110 | def test_syscall_entry(self): 111 | event = self.Event('syscall_entry_open') 112 | result = trace_utils.get_syscall_name(event) 113 | 114 | self.assertEqual(result, 'open') 115 | 116 | def test_not_syscall(self): 117 | event = self.Event('whatever') 118 | 119 | self.assertRaises(ValueError, trace_utils.get_syscall_name, event) 120 | -------------------------------------------------------------------------------- /tests/common/utils.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import os 24 | import time 25 | 26 | 27 | class TimezoneUtils(): 28 | def __init__(self): 29 | self.original_tz = None 30 | 31 | def set_up_timezone(self): 32 | # Make sure that the local timezone as seen by the time module 33 | # is the same regardless of where the test is actually 34 | # run. US/Eastern was picked arbitrarily. 35 | self.original_tz = os.environ.get('TZ') 36 | os.environ['TZ'] = 'US/Eastern' 37 | try: 38 | time.tzset() 39 | except AttributeError: 40 | print('Warning: time.tzset() is unavailable on Windows.' 41 | 'This may cause test failures.') 42 | 43 | def tear_down_timezone(self): 44 | # Restore the original value of TZ if any, else delete it from 45 | # the environment variables. 46 | if self.original_tz: 47 | os.environ['TZ'] = self.original_tz 48 | else: 49 | del os.environ['TZ'] 50 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Antoine Busque 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/integration/analysis_test.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Julien Desfossez 4 | # Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | import os 25 | import subprocess 26 | import unittest 27 | import locale 28 | from .trace_writer import TraceWriter 29 | 30 | 31 | class AnalysisTest(unittest.TestCase): 32 | COMMON_OPTIONS = '--no-color --no-progress --skip-validation --gmt' 33 | 34 | def __init__(self, *args, **kwargs): 35 | super().__init__(*args, **kwargs) 36 | self.rm_trace = True 37 | 38 | def set_up_class(self): 39 | dirname = os.path.dirname(os.path.realpath(__file__)) 40 | self.data_path = dirname + '/expected/' 41 | self.maxDiff = None 42 | self.trace_writer = TraceWriter() 43 | self.write_trace() 44 | 45 | def tear_down_class(self): 46 | if self.rm_trace: 47 | self.trace_writer.rm_trace() 48 | 49 | def write_trace(self): 50 | raise NotImplementedError 51 | 52 | def run(self, result=None): 53 | self.set_up_class() 54 | super().run(result) 55 | self.tear_down_class() 56 | 57 | return result 58 | 59 | def get_expected_output(self, test_name): 60 | expected_path = os.path.join(self.data_path, test_name + '.txt') 61 | with open(expected_path, 'r', encoding='utf-8') as expected_file: 62 | return expected_file.read() 63 | 64 | def _test_locale(self, locale_name): 65 | try: 66 | locale.setlocale(locale.LC_ALL, locale_name) 67 | return True 68 | except locale.Error: 69 | return False 70 | 71 | def _get_utf8_locale(self): 72 | # Test the two most common UTF-8 locales 73 | if self._test_locale('C.UTF-8'): 74 | return 'C.UTF-8' 75 | if self._test_locale('en_US.UTF-8'): 76 | return 'en_US.UTF-8' 77 | print('No supported UTF-8 locale detected') 78 | raise NameError 79 | 80 | def get_cmd_output(self, exec_name, options=''): 81 | cmd_fmt = './{} {} {} {}' 82 | cmd = cmd_fmt.format(exec_name, self.COMMON_OPTIONS, 83 | options, self.trace_writer.trace_root) 84 | 85 | # Create an utf-8 test env 86 | test_locale = self._get_utf8_locale() 87 | test_env = os.environ.copy() 88 | test_env['LC_ALL'] = test_locale 89 | 90 | process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, 91 | stderr=subprocess.STDOUT, env=test_env) 92 | output, unused_err = process.communicate() 93 | output = output.decode('utf-8') 94 | 95 | if output[-1:] == '\n': 96 | output = output[:-1] 97 | 98 | return output 99 | 100 | def save_test_result(self, result, test_name): 101 | result_path = os.path.join(self.trace_writer.trace_root, test_name) 102 | with open(result_path, 'w', encoding='utf-8') as result_file: 103 | result_file.write(result) 104 | self.rm_trace = False 105 | 106 | def _assertMultiLineEqual(self, result, expected, test_name): 107 | try: 108 | self.assertMultiLineEqual(result, expected) 109 | except AssertionError: 110 | self.save_test_result(result, test_name) 111 | raise 112 | -------------------------------------------------------------------------------- /tests/integration/expected/cputop.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:11.000000000] 2 | Per-TID Usage Process Migrations Priorities 3 | ################################################################################ 4 | ████████████████████████████████████████████████████████████████████████████████ 100.00 % prog100pc-cpu5 (42) 0 [20] 5 | ████████████████████ 25.00 % prog25pc-cpu1 (30665) 0 [20] 6 | ████████████████ 20.00 % prog20pc-cpu0 (30664) 0 [20] 7 | 0.00 % swapper/5 (0) 0 [] 8 | 9 | Per-CPU Usage 10 | ################################################################################ 11 | ████████████████ 21.00 % CPU 0 12 | ████████████████████████████████████████████████████ 66.00 % CPU 1 13 | ████████████████████████████████████████████████████████████████████████████████ 100.00 % CPU 5 14 | 15 | 16 | Total CPU Usage: 62.33% 17 | -------------------------------------------------------------------------------- /tests/integration/expected/disable_intersect.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.005000000, 1970-01-01 00:00:01.010000000] 2 | Soft IRQ Duration (us) Raise latency (us) 3 | count min avg max stdev | count min avg max stdev 4 | ----------------------------------------------------------------------------------|------------------------------------------------------------ 5 | 1: 1 3000.000 3000.000 3000.000 ? | 1 1000.000 1000.000 1000.000 ? -------------------------------------------------------------------------------- /tests/integration/expected/iolatencytop.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:01.024000000] 2 | 3 | Top system call latencies open (usec) 4 | Begin End Name Duration (usec) Size Proc PID Filename 5 | [00:00:01.023000000, 00:00:01.024000000] open 1000.000 N/A app3 101 test/open/file (fd=42) 6 | 7 | Top system call latencies read (usec) 8 | Begin End Name Duration (usec) Size Proc PID Filename 9 | [00:00:01.008000000, 00:00:01.009000000] read 1000.000 100 B app2 100 testfile (fd=3) 10 | [00:00:01.012000000, 00:00:01.013000000] read 1000.000 42 B app3 101 unknown (fd=3) 11 | 12 | Top system call latencies write (usec) 13 | Begin End Name Duration (usec) Size Proc PID Filename 14 | [00:00:01.004000000, 00:00:01.005000000] write 1000.000 10 B app 99 unknown (fd=4) -------------------------------------------------------------------------------- /tests/integration/expected/iousagetop.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:01.024000000] 2 | Per-process I/O Read Process Disk Net Unknown 3 | ################################################################################ 4 | ████████████████████████████████████████████████████████████████████████████████ 100 B app2 (100) 0 B 0 B 100 B 5 | █████████████████████████████████ 42 B app3 (unknown (tid=101)) 0 B 0 B 42 B 6 | 0 B app (99) 0 B 0 B 0 B 7 | 8 | Per-process I/O Write Process Disk Net Unknown 9 | ################################################################################ 10 | ████████████████████████████████████████████████████████████████████████████████ 10 B app (99) 0 B 0 B 10 B 11 | 0 B app2 (100) 0 B 0 B 0 B 12 | 0 B app3 (unknown (tid=101)) 0 B 0 B 0 B 13 | 14 | Per-file I/O Read Path 15 | ################################################################################ 16 | ████████████████████████████████████████████████████████████████████████████████ 100 B testfile 17 | █████████████████████████████████ 42 B unknown (app3) 18 | 19 | Per-file I/O Write Path 20 | ################################################################################ 21 | ████████████████████████████████████████████████████████████████████████████████ 10 B unknown (app) 22 | 23 | Block I/O Read Process 24 | ################################################################################ 25 | ████████████████████████████████████████████████████████████████████████████████ 5.00 KiB app (pid=99) 26 | 27 | Block I/O Write Process 28 | ################################################################################ 29 | ████████████████████████████████████████████████████████████████████████████████ 10.00 KiB app3 (pid=unknown (tid=101)) 30 | 31 | Disk Requests Sector Count Disk 32 | ################################################################################ 33 | ████████████████████████████████████████████████████████████████████████████████ 20 sectors (8,0) 34 | ████████████████████████████████████████ 10 sectors (252,0) 35 | 36 | Disk Request Count Disk 37 | ################################################################################ 38 | ████████████████████████████████████████████████████████████████████████████████ 1 requests (252,0) 39 | ████████████████████████████████████████████████████████████████████████████████ 1 requests (8,0) 40 | 41 | Disk Request Average Latency Disk 42 | ################################################################################ 43 | ████████████████████████████████████████████████████████████████████████████████ 1.00 ms (252,0) 44 | ████████████████████████████████████████████████████████████████████████████████ 1.00 ms (8,0) 45 | 46 | Network Received Bytes Interface 47 | ################################################################################ 48 | ████████████████████████████████████████████████████████████████████████████████ 200 B wlan0 49 | ████████████████████████████████████████ 100 B wlan1 50 | 51 | Network Sent Bytes Interface 52 | ################################################################################ 53 | ████████████████████████████████████████████████████████████████████████████████ 100 B wlan0 54 | 0 B wlan1 55 | -------------------------------------------------------------------------------- /tests/integration/expected/irqlog.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:01.045000000] 2 | Begin End Duration (us) CPU Type # Name 3 | [00:00:01.007000000, 00:00:01.008000000] 1000.000 1 SoftIRQ 1 TIMER_SOFTIRQ (raised at 00:00:01.000000000) 4 | [00:00:01.006000000, 00:00:01.009000000] 3000.000 3 SoftIRQ 1 TIMER_SOFTIRQ (raised at 00:00:01.001000000) 5 | [00:00:01.010000000, 00:00:01.012000000] 2000.000 1 SoftIRQ 9 RCU_SOFTIRQ (raised at 00:00:01.002000000) 6 | [00:00:01.011000000, 00:00:01.013000000] 2000.000 3 SoftIRQ 7 SCHED_SOFTIRQ (raised at 00:00:01.005000000) 7 | [00:00:01.014000000, 00:00:01.015000000] 1000.000 3 SoftIRQ 9 RCU_SOFTIRQ (raised at 00:00:01.004000000) 8 | [00:00:01.016000000, 00:00:01.018000000] 2000.000 0 IRQ 41 ahci 9 | [00:00:01.019000000, 00:00:01.020000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.017000000) 10 | [00:00:01.021000000, 00:00:01.023000000] 2000.000 0 IRQ 41 ahci 11 | [00:00:01.024000000, 00:00:01.025000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.022000000) 12 | [00:00:01.026000000, 00:00:01.028000000] 2000.000 0 IRQ 41 ahci 13 | [00:00:01.029000000, 00:00:01.030000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.027000000) 14 | [00:00:01.031000000, 00:00:01.033000000] 2000.000 0 IRQ 41 ahci 15 | [00:00:01.034000000, 00:00:01.035000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.032000000) 16 | [00:00:01.036000000, 00:00:01.038000000] 2000.000 0 IRQ 41 ahci 17 | [00:00:01.039000000, 00:00:01.040000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.037000000) 18 | [00:00:01.041000000, 00:00:01.043000000] 2000.000 0 IRQ 41 ahci 19 | [00:00:01.044000000, 00:00:01.045000000] 1000.000 0 SoftIRQ 4 BLOCK_SOFTIRQ (raised at 00:00:01.042000000) -------------------------------------------------------------------------------- /tests/integration/expected/irqstats.txt: -------------------------------------------------------------------------------- 1 | Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:01.045000000] 2 | Hard IRQ Duration (us) 3 | count min avg max stdev 4 | ----------------------------------------------------------------------------------| 5 | 41: 6 2000.000 2000.000 2000.000 0.000 | 6 | 7 | Soft IRQ Duration (us) Raise latency (us) 8 | count min avg max stdev | count min avg max stdev 9 | ----------------------------------------------------------------------------------|------------------------------------------------------------ 10 | 1: 2 1000.000 2000.000 3000.000 1414.214 | 2 5000.000 6000.000 7000.000 1414.214 11 | 4: 6 1000.000 1000.000 1000.000 0.000 | 6 2000.000 2000.000 2000.000 0.000 12 | 7: 1 2000.000 2000.000 2000.000 ? | 1 6000.000 6000.000 6000.000 ? 13 | 9: 2 1000.000 1500.000 2000.000 707.107 | 2 8000.000 9000.000 10000.000 1414.214 -------------------------------------------------------------------------------- /tests/integration/expected/no_intersection.txt: -------------------------------------------------------------------------------- 1 | Error: Trace has no intersection. Use --no-intersection to override -------------------------------------------------------------------------------- /tests/integration/gen_ctfwriter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (C) 2016 - Julien Desfossez 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # Helper tool to generate CTFWriter code from the metadata of an existing 26 | # trace. 27 | # It used to add code in TraceTest.py. 28 | # Only the basic types are supported, a warning is generated if a field cannot 29 | # be generated so it is easy to look manually at the metadata and fix it. 30 | 31 | import sys 32 | import argparse 33 | 34 | from babeltrace import TraceCollection, CTFScope, CTFTypeId 35 | 36 | 37 | def sanitize(s): 38 | """Replace special characters in s by underscores. 39 | 40 | This makes s suitable to use in code as a function or variable name. 41 | """ 42 | s = s.replace(':', '_') 43 | 44 | return s 45 | 46 | 47 | def get_definition_type(field, event): 48 | event_name = sanitize(event.name) 49 | 50 | if field.type == CTFTypeId.INTEGER: 51 | signed = '' 52 | if field.signedness == 0: 53 | signed = 'u' 54 | length = field.length 55 | print(' self.%s.add_field(self.%sint%s_type, "_%s")' % 56 | (event_name, signed, length, field.name)) 57 | elif field.type == CTFTypeId.ARRAY: 58 | print(' self.%s.add_field(self.array%s_type, "_%s")' % 59 | (event_name, field.length, field.name)) 60 | elif field.type == CTFTypeId.STRING: 61 | print(' self.%s.add_field(self.string_type, "_%s")' % 62 | (event_name, field.name)) 63 | else: 64 | print(' # FIXME %s.%s: Unhandled type %d' % (event.name, 65 | field.name, 66 | field.type)) 67 | 68 | 69 | def gen_define(event): 70 | fields = [] 71 | event_name = sanitize(event.name) 72 | print(' def define_%s(self):' % (event_name)) 73 | print(' self.%s = CTFWriter.EventClass("%s")' % 74 | (event_name, event.name)) 75 | for field in event.fields: 76 | if field.scope == CTFScope.EVENT_FIELDS: 77 | fname = field.name 78 | fields.append(fname) 79 | get_definition_type(field, event) 80 | print(' self.add_event(self.%s)' % event_name) 81 | print('') 82 | return fields 83 | 84 | 85 | def gen_write(event, fields): 86 | f_list = '' 87 | for f in fields: 88 | f_list += ', {}'.format(f) 89 | 90 | event_name = sanitize(event.name) 91 | print(' def write_%s(self, time_ms, cpu_id%s):' % (event_name, 92 | f_list)) 93 | print(' event = CTFWriter.Event(self.%s)' % (event_name)) 94 | print(' self.clock.time = time_ms * 1000000') 95 | print(' self.set_int(event.payload("_cpu_id"), cpu_id)') 96 | for field in event.fields: 97 | if field.scope == CTFScope.EVENT_FIELDS: 98 | fname = field.name 99 | if field.type == CTFTypeId.INTEGER: 100 | print(' self.set_int(event.payload("_%s"), %s)' % 101 | (fname, fname)) 102 | elif field.type == CTFTypeId.ARRAY: 103 | print(' self.set_char_array(event.payload("_%s"), ' 104 | '%s)' % (fname, fname)) 105 | elif field.type == CTFTypeId.STRING: 106 | print(' self.set_string(event.payload("_%s"), %s)' % 107 | (fname, fname)) 108 | else: 109 | print(' # FIXME %s.%s: Unhandled type %d' % 110 | (event.name, field.name, field.type)) 111 | print(' self.stream.append_event(event)') 112 | print(' self.stream.flush()') 113 | print('') 114 | 115 | 116 | def gen_parser(handle, args): 117 | for h in handle.values(): 118 | for event in h.events: 119 | fields = gen_define(event) 120 | gen_write(event, fields) 121 | 122 | 123 | if __name__ == "__main__": 124 | parser = argparse.ArgumentParser(description='CTFWriter code generator') 125 | parser.add_argument('path', metavar="", help='Trace path') 126 | args = parser.parse_args() 127 | 128 | traces = TraceCollection() 129 | handle = traces.add_traces_recursive(args.path, "ctf") 130 | if handle is None: 131 | sys.exit(1) 132 | 133 | gen_parser(handle, args) 134 | 135 | for h in handle.values(): 136 | traces.remove_trace(h) 137 | -------------------------------------------------------------------------------- /tests/integration/test_cputop.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Julien Desfossez 4 | # Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from .analysis_test import AnalysisTest 25 | 26 | 27 | class CpuTest(AnalysisTest): 28 | def write_trace(self): 29 | # runs the whole time: 100% 30 | self.trace_writer.write_sched_switch(1000, 5, 'swapper/5', 31 | 0, 'prog100pc-cpu5', 42) 32 | # runs for 2s alternating with swapper out every 100ms 33 | self.trace_writer.sched_switch_50pc(1100, 5000, 0, 100, 'swapper/0', 34 | 0, 'prog20pc-cpu0', 30664) 35 | # runs for 2.5s alternating with swapper out every 100ms 36 | self.trace_writer.sched_switch_50pc(5100, 10000, 1, 100, 'swapper/1', 37 | 0, 'prog25pc-cpu1', 30665) 38 | # switch out prog100pc-cpu5 39 | self.trace_writer.write_sched_switch(11000, 5, 'prog100pc-cpu5', 40 | 42, 'swapper/5', 0) 41 | self.trace_writer.flush() 42 | 43 | def test_cputop(self): 44 | test_name = 'cputop' 45 | expected = self.get_expected_output(test_name) 46 | result = self.get_cmd_output('lttng-cputop', 47 | options='--no-intersection') 48 | 49 | self._assertMultiLineEqual(result, expected, test_name) 50 | -------------------------------------------------------------------------------- /tests/integration/test_intersect.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Julien Desfossez 4 | # Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | import unittest 25 | from lttnganalyses.common import trace_utils 26 | from .analysis_test import AnalysisTest 27 | 28 | 29 | class IntersectTest(AnalysisTest): 30 | def write_trace(self): 31 | # Write these events in the default stream. 32 | self.trace_writer.write_softirq_raise(1005, 3, 1) 33 | self.trace_writer.write_softirq_entry(1006, 3, 1) 34 | self.trace_writer.write_softirq_exit(1009, 3, 1) 35 | 36 | # Override the default stream, so all new events are written 37 | # in a different stream, no overlapping timestamps between streams. 38 | self.trace_writer.create_stream() 39 | self.trace_writer.write_softirq_exit(1010, 2, 7) 40 | self.trace_writer.flush() 41 | 42 | @unittest.skipIf(trace_utils.read_babeltrace_version() < 43 | trace_utils.BT_INTERSECT_VERSION, 44 | "not supported by Babeltrace < %s" % 45 | trace_utils.BT_INTERSECT_VERSION,) 46 | def test_no_intersection(self): 47 | test_name = 'no_intersection' 48 | expected = self.get_expected_output(test_name) 49 | result = self.get_cmd_output('lttng-irqstats') 50 | 51 | self._assertMultiLineEqual(result, expected, test_name) 52 | 53 | def test_disable_intersect(self): 54 | test_name = 'disable_intersect' 55 | expected = self.get_expected_output(test_name) 56 | result = self.get_cmd_output('lttng-irqstats', 57 | options='--no-intersection') 58 | 59 | self._assertMultiLineEqual(result, expected, test_name) 60 | -------------------------------------------------------------------------------- /tests/integration/test_io.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Julien Desfossez 4 | # Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from .analysis_test import AnalysisTest 25 | 26 | 27 | class IoTest(AnalysisTest): 28 | def write_trace(self): 29 | # app (99) is known at statedump 30 | self.trace_writer.write_lttng_statedump_process_state( 31 | 1000, 0, 99, 99, 99, 99, 98, 98, 'app', 0, 5, 0, 5, 0) 32 | # app2 (100) unknown at statedump has testfile, FD 3 defined at 33 | # statedump 34 | self.trace_writer.write_lttng_statedump_file_descriptor( 35 | 1001, 0, 100, 3, 0, 0, 'testfile') 36 | # app write 10 bytes to FD 4 37 | self.trace_writer.write_sched_switch(1002, 0, 'swapper/0', 0, 'app', 38 | 99) 39 | self.trace_writer.write_syscall_write(1004, 0, 1, 4, 0xabcd, 10, 10) 40 | # app2 reads 100 bytes in FD 3 41 | self.trace_writer.write_sched_switch(1006, 0, 'app', 99, 'app2', 100) 42 | self.trace_writer.write_syscall_read(1008, 0, 1, 3, 0xcafe, 100, 100) 43 | # app3 and its FD 3 are completely unknown at statedump, tries to read 44 | # 100 bytes from FD 3 but only gets 42 45 | self.trace_writer.write_sched_switch(1010, 0, 'app2', 100, 'app3', 101) 46 | self.trace_writer.write_syscall_read(1012, 0, 1, 3, 0xcafe, 100, 42) 47 | # block write 48 | self.trace_writer.write_block_rq_issue(1015, 0, 264241152, 33, 10, 40, 49 | 99, 0, 0, '', 'app') 50 | self.trace_writer.write_block_rq_complete(1016, 0, 264241152, 33, 10, 51 | 0, 0, 0, '') 52 | # block read 53 | self.trace_writer.write_block_rq_issue(1017, 0, 8388608, 33, 20, 90, 54 | 101, 1, 0, '', 'app3') 55 | self.trace_writer.write_block_rq_complete(1018, 0, 8388608, 33, 20, 0, 56 | 1, 0, '') 57 | # net xmit 58 | self.trace_writer.write_net_dev_xmit(1020, 2, 0xff, 32, 100, 'wlan0') 59 | # net receive 60 | self.trace_writer.write_netif_receive_skb(1021, 1, 0xff, 100, 'wlan1') 61 | self.trace_writer.write_netif_receive_skb(1022, 1, 0xff, 200, 'wlan0') 62 | # syscall open 63 | self.trace_writer.write_syscall_open(1023, 0, 1, 'test/open/file', 0, 64 | 0, 42) 65 | self.trace_writer.flush() 66 | 67 | def test_iousagetop(self): 68 | test_name = 'iousagetop' 69 | expected = self.get_expected_output(test_name) 70 | result = self.get_cmd_output('lttng-iousagetop', 71 | options='--no-intersection') 72 | 73 | self._assertMultiLineEqual(result, expected, test_name) 74 | 75 | def test_iolatencytop(self): 76 | test_name = 'iolatencytop' 77 | expected = self.get_expected_output(test_name) 78 | result = self.get_cmd_output('lttng-iolatencytop', 79 | options='--no-intersection') 80 | 81 | self._assertMultiLineEqual(result, expected, test_name) 82 | -------------------------------------------------------------------------------- /tests/integration/test_irq.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (C) 2016 - Julien Desfossez 4 | # Antoine Busque 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from .analysis_test import AnalysisTest 25 | 26 | 27 | class IrqTest(AnalysisTest): 28 | def write_trace(self): 29 | self.trace_writer.write_softirq_raise(1000, 1, 1) 30 | self.trace_writer.write_softirq_raise(1001, 3, 1) 31 | self.trace_writer.write_softirq_raise(1002, 1, 9) 32 | self.trace_writer.write_softirq_exit(1003, 0, 4) 33 | self.trace_writer.write_softirq_raise(1004, 3, 9) 34 | self.trace_writer.write_softirq_raise(1005, 3, 7) 35 | self.trace_writer.write_softirq_entry(1006, 3, 1) 36 | self.trace_writer.write_softirq_entry(1007, 1, 1) 37 | self.trace_writer.write_softirq_exit(1008, 1, 1) 38 | self.trace_writer.write_softirq_exit(1009, 3, 1) 39 | self.trace_writer.write_softirq_entry(1010, 1, 9) 40 | self.trace_writer.write_softirq_entry(1011, 3, 7) 41 | self.trace_writer.write_softirq_exit(1012, 1, 9) 42 | self.trace_writer.write_softirq_exit(1013, 3, 7) 43 | self.trace_writer.write_softirq_entry(1014, 3, 9) 44 | self.trace_writer.write_softirq_exit(1015, 3, 9) 45 | self.trace_writer.write_irq_handler_entry(1016, 0, 41, 'ahci') 46 | self.trace_writer.write_softirq_raise(1017, 0, 4) 47 | self.trace_writer.write_irq_handler_exit(1018, 0, 41, 1) 48 | self.trace_writer.write_softirq_entry(1019, 0, 4) 49 | self.trace_writer.write_softirq_exit(1020, 0, 4) 50 | self.trace_writer.write_irq_handler_entry(1021, 0, 41, 'ahci') 51 | self.trace_writer.write_softirq_raise(1022, 0, 4) 52 | self.trace_writer.write_irq_handler_exit(1023, 0, 41, 1) 53 | self.trace_writer.write_softirq_entry(1024, 0, 4) 54 | self.trace_writer.write_softirq_exit(1025, 0, 4) 55 | self.trace_writer.write_irq_handler_entry(1026, 0, 41, 'ahci') 56 | self.trace_writer.write_softirq_raise(1027, 0, 4) 57 | self.trace_writer.write_irq_handler_exit(1028, 0, 41, 1) 58 | self.trace_writer.write_softirq_entry(1029, 0, 4) 59 | self.trace_writer.write_softirq_exit(1030, 0, 4) 60 | self.trace_writer.write_irq_handler_entry(1031, 0, 41, 'ahci') 61 | self.trace_writer.write_softirq_raise(1032, 0, 4) 62 | self.trace_writer.write_irq_handler_exit(1033, 0, 41, 1) 63 | self.trace_writer.write_softirq_entry(1034, 0, 4) 64 | self.trace_writer.write_softirq_exit(1035, 0, 4) 65 | self.trace_writer.write_irq_handler_entry(1036, 0, 41, 'ahci') 66 | self.trace_writer.write_softirq_raise(1037, 0, 4) 67 | self.trace_writer.write_irq_handler_exit(1038, 0, 41, 1) 68 | self.trace_writer.write_softirq_entry(1039, 0, 4) 69 | self.trace_writer.write_softirq_exit(1040, 0, 4) 70 | self.trace_writer.write_irq_handler_entry(1041, 0, 41, 'ahci') 71 | self.trace_writer.write_softirq_raise(1042, 0, 4) 72 | self.trace_writer.write_irq_handler_exit(1043, 0, 41, 1) 73 | self.trace_writer.write_softirq_entry(1044, 0, 4) 74 | self.trace_writer.write_softirq_exit(1045, 0, 4) 75 | self.trace_writer.flush() 76 | 77 | def test_irqstats(self): 78 | test_name = 'irqstats' 79 | expected = self.get_expected_output(test_name) 80 | result = self.get_cmd_output('lttng-irqstats', 81 | options='--no-intersection') 82 | 83 | self._assertMultiLineEqual(result, expected, test_name) 84 | 85 | def test_irqlog(self): 86 | test_name = 'irqlog' 87 | expected = self.get_expected_output(test_name) 88 | result = self.get_cmd_output('lttng-irqlog', 89 | options='--no-intersection') 90 | 91 | self._assertMultiLineEqual(result, expected, test_name) 92 | -------------------------------------------------------------------------------- /tests_long_regression/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lttng/lttng-analyses/0c87b2d3284253c92083355dff6d1df29ee421a3/tests_long_regression/__init__.py -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 1.9 3 | envlist = py3,pep8 4 | skipsdist = True 5 | toxworkdir = {env:TOXWORKDIR:.tox} 6 | passenv=BABELTRACE_* 7 | 8 | [testenv] 9 | skip_install = True 10 | sitepackages = True 11 | setenv = 12 | PYTHONPATH = {env:PYTHONPATH:} 13 | deps = -r{toxinidir}/requirements.txt 14 | -r{toxinidir}/test-requirements.txt 15 | commands = py.test --cov-config .coveragerc --cov=lttnganalyses --basetemp={envtmpdir} tests {posargs} 16 | 17 | [testenv:noutf8] 18 | setenv = 19 | LC_ALL=C 20 | PYTHONPATH = {env:PYTHONPATH:} 21 | commands = py.test --cov-config .coveragerc --cov=lttnganalyses --basetemp={envtmpdir} tests {posargs} 22 | 23 | [testenv:pep8] 24 | commands = flake8 --ignore=E123,E125,W503,W504 25 | 26 | [testenv:longregression] 27 | commands = py.test --cov-config .coveragerc --cov=lttnganalyses --basetemp={envtmpdir} tests_long_regression {posargs} 28 | 29 | [flake8] 30 | # E123, E125 skipped as they are invalid PEP-8. 31 | 32 | show-source = True 33 | ignore = E123,E125,W503,W504 34 | builtins = _ 35 | exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,versioneer.py,lttnganalyses/_version.py,tests/__init__.py 36 | 37 | [testenv:pylint-errors] 38 | deps = pylint >= 1.6 39 | commands = pylint -f colorized -E lttnganalyses 40 | 41 | [testenv:pylint-warnings] 42 | deps = pylint >= 1.6 43 | commands = pylint -f colorized -d all -e W -r n lttnganalyses 44 | 45 | [testenv:pylint-full] 46 | deps = pylint >= 1.6 47 | commands = pylint -f colorized --disable=all -e R,E,W lttnganalyses 48 | --------------------------------------------------------------------------------