├── .github └── workflows │ ├── cibuildwheel.yml │ └── tests.yml ├── .gitignore ├── BUGS ├── LICENSE ├── MANIFEST.in ├── README-develop.md ├── README.md ├── dev_requirements.txt ├── docs ├── Makefile ├── _static │ └── vmprof-logo.png ├── conf.py ├── data.rst ├── development.rst ├── faq.rst ├── format.rst ├── index.rst ├── jitlog.rst ├── native.rst ├── query.rst └── vmprof.rst ├── example.py ├── jitlog ├── __init__.py ├── __main__.py ├── constants.py ├── marks.py ├── merge_point.py ├── objects.py ├── parser.py ├── prettyprinter.py ├── query.py ├── test │ ├── __init__.py │ ├── data │ │ ├── code.py │ │ └── code2.py │ ├── test_encoding.py │ ├── test_jitlog.py │ └── test_query.py └── upload.py ├── pytest.ini ├── setup.cfg ├── setup.py ├── src ├── _vmprof.c ├── _vmprof.h ├── compat.c ├── compat.h ├── khash.h ├── libbacktrace │ ├── ChangeLog │ ├── ChangeLog.jit │ ├── Makefile.am │ ├── Makefile.in │ ├── README │ ├── aclocal.m4 │ ├── alloc.c │ ├── ansidecl.h │ ├── atomic.c │ ├── backtrace-supported.h │ ├── backtrace-supported.h.in │ ├── backtrace.c │ ├── backtrace.h │ ├── btest.c │ ├── config.guess │ ├── config.h │ ├── config.h.in │ ├── config.sub │ ├── configure │ ├── configure.ac │ ├── dwarf.c │ ├── dwarf2.def │ ├── dwarf2.h │ ├── elf.c │ ├── fileline.c │ ├── filenames.h │ ├── filetype.awk │ ├── hashtab.h │ ├── install-sh │ ├── internal.h │ ├── mmap.c │ ├── mmapio.c │ ├── nounwind.c │ ├── pecoff.c │ ├── posix.c │ ├── print.c │ ├── read.c │ ├── simple.c │ ├── sort.c │ ├── stamp-h1 │ ├── state.c │ ├── stest.c │ └── unknown.c ├── machine.c ├── machine.h ├── msiinttypes │ ├── changelog.txt │ ├── inttypes.h │ └── stdint.h ├── populate_frames.c ├── populate_frames.h ├── symboltable.c ├── symboltable.h ├── trampoline.c ├── trampoline.h ├── unwind │ └── vmprof_unwind.h ├── vmp_stack.c ├── vmp_stack.h ├── vmprof.h ├── vmprof_common.c ├── vmprof_common.h ├── vmprof_config.h ├── vmprof_get_custom_offset.h ├── vmprof_getpc.h ├── vmprof_memory.c ├── vmprof_memory.h ├── vmprof_mt.c ├── vmprof_mt.h ├── vmprof_unix.c ├── vmprof_unix.h ├── vmprof_win.c └── vmprof_win.h ├── test_requirements.txt ├── tox.ini ├── vmprof ├── __init__.py ├── __main__.py ├── cli.py ├── profiler.py ├── reader.py ├── show.py ├── stats.py ├── test │ ├── __init__.py │ ├── cpuburn.py │ ├── richards.cpython.prof │ ├── richards.pypy.prof │ ├── simple_nested.pypy.prof │ ├── simple_nested.pypy.prof.libcache │ ├── sqlite.py │ ├── test_c_source.py │ ├── test_c_symboltable.py │ ├── test_config.py │ ├── test_reader.py │ ├── test_run.py │ ├── test_stats.py │ └── wsgi.prof └── upload.py ├── vmprofdemo.py └── vmshare ├── __init__.py ├── binary.py └── service.py /.github/workflows/cibuildwheel.yml: -------------------------------------------------------------------------------- 1 | name: build-wheels 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ master, unstuck-vmprof ] 8 | pull_request: 9 | branches: [ master, unstuck-vmprof ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build_binary_wheels: 16 | name: Build binary wheels on ${{ matrix.os }} 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-24.04, windows-2022, macos-latest] 22 | 23 | steps: 24 | # Note: the action happens inside a docker image 25 | - uses: actions/checkout@v4 26 | 27 | - name: Set up QEMU 28 | if: runner.os == 'Linux' 29 | uses: docker/setup-qemu-action@v3 30 | with: 31 | platforms: all 32 | 33 | - name: Build wheels 34 | uses: pypa/cibuildwheel@v2.22.0 35 | env: 36 | CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 37 | CIBW_MANYLINUX_I686_IMAGE: manylinux2014 38 | CIBW_MANYLINUX_AARCH64_IMAGE: manylinux2014 39 | CIBW_ARCHS_LINUX: auto aarch64 40 | CIBW_SKIP: "cp36-* pp* *-win32 *-manylinux_i686 *musllinux*" 41 | CIBW_BEFORE_BUILD_LINUX: yum install -y libunwind-devel elfutils-libelf-devel libdwarf-devel 42 | CIBW_BEFORE_TEST: pip install -r test_requirements.txt 43 | CIBW_TEST_COMMAND: cd {package} && pytest vmprof jitlog -vv 44 | CIBW_TEST_COMMAND_WINDOWS: cd /d {package} && pytest vmprof jitlog -vv 45 | CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x}" 46 | 47 | - uses: actions/upload-artifact@v4 48 | with: 49 | name: ${{ matrix.os }} 50 | path: ./wheelhouse/*.whl 51 | 52 | 53 | build_pypy_wheels: 54 | name: Build pypy wheels 55 | runs-on: ubuntu-24.04 56 | strategy: 57 | fail-fast: false 58 | 59 | steps: 60 | # Note: not inside a docker 61 | - uses: actions/checkout@v4 62 | - uses: actions/setup-python@v5 63 | with: 64 | python-version: 'pypy-3.10' 65 | 66 | - name: Install system libraries 67 | run: sudo apt install -y libunwind-dev libelf-dev libdwarf-dev rename 68 | 69 | - name: Build wheel 70 | run: | 71 | pypy -m pip install wheel 72 | pypy -m pip wheel . 73 | 74 | - name: Install wheel 75 | run: pypy -m pip install vmprof*.whl 76 | 77 | - name: Test wheel 78 | run: | 79 | FAILED=false 80 | pypy -m pip install -r test_requirements.txt build 81 | pypy -m pytest vmprof -v || FAILED=true 82 | pypy -m pytest jitlog -v || FAILED=true 83 | if [ "FAILED" == true ]; then exit 1; fi 84 | # The wheel name is something like 85 | # vmprof-0.4.15-py3-none-any.whl 86 | # when it should be 87 | # vmprof-0.4.15-pp3-none-any.whl 88 | rename 's/py3/pp3/' vmprof-*.whl 89 | 90 | - name: Build sdist 91 | run: | 92 | pypy -m build --sdist . 93 | 94 | 95 | - uses: actions/upload-artifact@v4 96 | with: 97 | path: | 98 | vmprof*.whl 99 | dist/*.tar.gz 100 | 101 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [master, unstuck-vmprof] 6 | pull_request: 7 | branches: [master, unstuck-vmprof] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | runs-on: ${{ matrix.os }} 13 | permissions: 14 | pull-requests: write 15 | name: ${{ matrix.os }} - ${{ matrix.python }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | # Test all supported versions on Ubuntu: 20 | os: [ubuntu-latest] 21 | python: ["3.9", "3.10", "3.11", "pypy-3.10"] 22 | experimental: [false] 23 | # include: 24 | # - os: macos-latest 25 | # python: "3.10" 26 | # experimental: false 27 | # - os: windows-latest 28 | # python: "3.10" 29 | # experimental: false 30 | steps: 31 | - uses: actions/checkout@v4 32 | - name: Install libunwind 33 | run: | 34 | sudo apt install -y libunwind-dev 35 | pkg-config libunwind --cflags --libs-only-l 36 | - name: Set up Python ${{ matrix.python }} 37 | uses: actions/setup-python@v5 38 | with: 39 | python-version: ${{ matrix.python }} 40 | - name: Install 41 | run: | 42 | python -m pip install --upgrade pip setuptools 43 | python -m pip install -e . 44 | python -m pip install -r test_requirements.txt 45 | - name: Display Python version 46 | run: python -c "import sys; print(sys.version)" 47 | - name: Run Tests 48 | id: vmprof 49 | run: python -m pytest -v vmprof jitlog 50 | 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | *.sqlite3 3 | *.virtualenv 4 | __pycache__/ 5 | *.py[cod] 6 | 7 | # backup 8 | *~ 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | env/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | *.dat 62 | *.swp 63 | *.swo 64 | 65 | _test_*.o 66 | _test_*.c 67 | 68 | # ./configure output 69 | src/libbacktrace/Makefile 70 | src/libbacktrace/config.status 71 | src/libbacktrace/gstdint.h 72 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | On windows, we handle all the threads, not just ones that are running. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | 5 | The MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation 9 | files (the "Software"), to deal in the Software without 10 | restriction, including without limitation the rights to use, 11 | copy, modify, merge, publish, distribute, sublicense, and/or 12 | sell copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included 16 | in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | 26 | vmprof copyright holders 2015-2016 27 | ---------------------------------- 28 | 29 | Antonio Cuni 30 | Maciej Fijalkowski 31 | Armin Rigo 32 | Jetbrains s.r.o. 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src *.h *.c 2 | include src/libbacktrace/* 3 | include LICENSE 4 | recursive-exclude vmprof/test * 5 | recursive-exclude jitlog/test * 6 | -------------------------------------------------------------------------------- /README-develop.md: -------------------------------------------------------------------------------- 1 | vmprof is a delicate piece of software. Following considerations should 2 | be present when developing it. 3 | 4 | Supported platform combinations (all combinations are supported): 5 | 6 | * pypy (>=4.1), cpython 7 | 8 | * windows, os x and linux 9 | 10 | CPython should be tested on both TeamCity and Travis, PyPy is more 11 | patchy since there is never a new enough version on either. Since PyPy 12 | only exercises the pure python part, please test it each time you change 13 | and interface between `_vmprof` and `vmprof`. 14 | 15 | ## Signals 16 | 17 | On OS X and Linux we handle signal handlers. This means that we have 18 | to be very very careful at what we can and cannot do. Notably, we can't 19 | use malloc, locks or refcounting in any of the signal handlers. Python data 20 | should be read-only and we should be prepared to read garbage or NULL 21 | from anything. 22 | 23 | On windows we use an external thread, so it's an imperative we freeze 24 | the thread we're inspecting. It's possible that the interpreter state 25 | handling is not thread safe in a way it should be, investigate how we 26 | can improve it. 27 | -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | six 2 | requests 3 | backports.shutil_which 4 | pytz 5 | colorama 6 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/vmprof.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/vmprof.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/vmprof" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/vmprof" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/_static/vmprof-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/docs/_static/vmprof-logo.png -------------------------------------------------------------------------------- /docs/data.rst: -------------------------------------------------------------------------------- 1 | Data sent to vmprof.com 2 | ======================= 3 | 4 | We only send the bare essentials to `vmprof.com`_. This package is no spy software. 5 | 6 | It includes the following data: 7 | 8 | * The full command line 9 | * The name of the interpreter used 10 | * Filesystem path names, function names and line numbers of to your scripts 11 | * Generic system information (Operating system, CPU word size, ...) 12 | 13 | If jit log data is sent (--jitlog) on PyPy the following is also included: 14 | 15 | * Meta data the JIT compiler produces. E.g. IR operations, Machine code 16 | * Source code snippets: `vmprof.com`_ will receive source lines of your program. Only those are transmitted that ran often enough to trigger the JIT compiler to optimize your program. 17 | 18 | .. _`vmprof.com`: http://vmprof.com 19 | .. _`PyPy`: http://pypy.org 20 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | Develop VMProf 2 | ============== 3 | 4 | VMProf consists of several projects working together: 5 | 6 | * `vmprof-python`_: The PyPI package providing the command line interface to enable vmprof. 7 | * `vmprof-server`_: Webservice hosted at `vmprof.com`_. Hosts and visualizes data uploaded by `vmprof-python`_ package. 8 | * `vmprof-integration`_: Test suite for pulling together all different projects and ensuring that all play together nicely. 9 | * `PyPy`_: A virtual machine for the Python programming language. Most notably it contains an implementation for the logging facility `vmprof-server`_ can display. 10 | 11 | The following description helps you to set up a development environment on Linux. For Windows 12 | and MacOSX the instructions might be similar. 13 | 14 | .. _`PyPy`: http://pypy.org 15 | .. _`vmprof.com`: http://vmprof.com 16 | .. _`vmprof-python`: https://github.com/vmprof/vmprof-python 17 | .. _`vmprof-server`: https://github.com/vmprof/vmprof-server 18 | .. _`vmprof-integration`: https://github.com/vmprof/vmprof-integration 19 | 20 | Develop VMProf on Linux 21 | ----------------------- 22 | 23 | It is recommended to use Python 3.x for development. Here is a list of requirements 24 | on your system: 25 | 26 | * python 27 | * sqlite3 28 | * virtualenv 29 | 30 | Please move you shell to the location you store your source code in and setup 31 | a virtual environment:: 32 | 33 | $ virtualenv -p /usr/bin/python3 vmprof3 34 | $ source vmprof3/bin/activate 35 | 36 | All commands from now on assume you have the vmprof3 virutal environment enabled. 37 | 38 | Clone the repositories 39 | ---------------------- 40 | 41 | :: 42 | 43 | $ git clone git@github.com:vmprof/vmprof-integration.git 44 | $ git clone git@github.com:vmprof/vmprof-server.git 45 | $ git clone git@github.com:vmprof/vmprof-python.git 46 | # on old mercurial version the following command takes ages. please use a recent version 47 | $ hg clone ssh://hg@bitbucket.org/pypy/pypy # optional, only if you want to hack on pypy as well 48 | 49 | VMProf Server 50 | ------------- 51 | 52 | :: 53 | 54 | # setup django service 55 | $ cd vmprof-server 56 | $ pip install -r requirements/development.txt 57 | $ python manage.py migrate 58 | # to run the service 59 | $ python manage.py runserver -v 3 60 | 61 | VMProf Python 62 | ------------- 63 | 64 | An optional stage. It is only necessary if you want to co develop `vmprof-python`_ with `vmprof-server`_:: 65 | 66 | # install vmprof for development (only needed if you want to co develop vmprof-python) 67 | $ cd vmprof-python 68 | $ python setup.py develop 69 | 70 | 71 | Now you are able to change both the python package and the server and see the results. 72 | Here are some more hints on how to develop this platform 73 | 74 | Smaller Profiles 75 | ---------------- 76 | 77 | Some times it is tedious to generate a big log file and develop a new feature with it. 78 | Both for VMProf and JitLog you can generate small log files that ease development. 79 | 80 | There are small logs generated by a python script in `vmprof-server/vmlog/test/data/loggen.py`. Use the following command to load those:: 81 | 82 | $ ./manage.py loaddata vmlog/test/fixtures.yaml 83 | 84 | Now open your browser and redirect them to the jitlog. E.g. http://localhost:8000/#/1v1/traces 85 | 86 | Integration Tests 87 | ----------------- 88 | 89 | This is a very important test suite to ensure that all packages work together. It is automatically run every day by travis. You can run them locally. If you happen not to run a Debian base distribution, you can provide the following shell variable to prevent the tests from downloading a Debian PyPy:: 90 | 91 | $ TEST_PYPY_EXEC=/path/to/pypy py.test testvmprof/ 92 | 93 | 94 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions 2 | ========================== 3 | 4 | * **What does mean?**: Debugging information might or might not be compiled 5 | with some libraries. If you see lots of those entries you might want to compile the libraries to include 6 | dwarf debugging information. In most cases ``gcc -g ...`` will help. 7 | If the symbol has been exported in the shared object (on linux), ``dladdr`` might still be able to extract 8 | the function name even if no debugging information has been attached. 9 | 10 | * **Is it possible to just profile a part of my program?**: Yes here an example how you could do just that:: 11 | 12 | with open('test.prof', 'w+b') as fd: 13 | vmprof.enable(fd.fileno()) 14 | my_function_or_program() 15 | vmprof.disable() 16 | 17 | Upload it later to vmprof.com if you choose to inspect it further:: 18 | 19 | $ python -m vmprof.upload test.prof 20 | 21 | 22 | 23 | * **What do the colors on vmprof.com mean?**: For plain CPython there is no particular meaning, we might change 24 | that in the future. For PyPy we have a color coding to show at which state the VM sampled (e.g. JIT, Warmup, ...). 25 | 26 | * **My Windows profile is malformed?**: Please ensure that you open the file in binary mode. Otherwise Windows 27 | will transform ``\n`` to ``\r\n``. 28 | 29 | * **Do I need to install libunwind?**: Usually not. We ship python wheels that bundle libunwind shared objects. If you install vmprof from source, then you need to install the development headers of your distribution. OSX ships libunwind per default. If your pip version is really old it does not pull wheels and it will end up compiling from source. 30 | 31 | -------------------------------------------------------------------------------- /docs/format.rst: -------------------------------------------------------------------------------- 1 | Profile File Formats 2 | ==================== 3 | 4 | This project incooperates several custom tailored file formats. 5 | Most notably the CPU & Memory profile format and a file format 6 | for the JIT log. 7 | 8 | Both share the same setup:: 9 | 10 | <8-bit tag> 11 | <8-bit tag> 12 | ... 13 | 14 | The tag decides how to proceed with the content. 15 | 16 | CPU & Memory Profile 17 | -------------------- 18 | 19 | * Adress mapping: Matches the following pattern:: 20 | 21 | ::: 22 | 23 | Most commonly lang will be `py`, but also can be `n` for 24 | native symbols. 25 | `line` is a positive integer number. 26 | `file` a path name, or '-' if no file could be found. 27 | 28 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | 2 | .. image:: _static/vmprof-logo.png 3 | :width: 386px 4 | :align: center 5 | 6 | 7 | | 8 | | 9 | 10 | VMProf Platform 11 | =============== 12 | 13 | `vmprof`_ is a platform to understand and resolve performance bottlenecks in your code. 14 | It includes a *lightweight profiler* for `CPython`_ 2.7, `CPython`_ 3 and `PyPy`_ 15 | and an assembler log visualizer for `PyPy`_. Currently we support Linux, Mac OS X and Windows. 16 | 17 | The following provides more information about CPU profiles and JIT Compiler Logs: 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | 22 | vmprof 23 | faq 24 | development 25 | native 26 | format 27 | jitlog 28 | query 29 | data 30 | 31 | .. _`CPython`: http://python.org 32 | .. _`PyPy`: http://pypy.org 33 | .. _`vmprof`: https://github.com/vmprof/vmprof-python 34 | .. _`statistical profiler`: https://en.wikipedia.org/wiki/Profiling_(computer_programming)#Statistical_profilers 35 | .. _`gperftools`: https://code.google.com/p/gperftools/ 36 | .. _`vtune`: https://software.intel.com/en-us/intel-vtune-amplifier-xe 37 | -------------------------------------------------------------------------------- /docs/jitlog.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | JIT Compiler Logs 3 | ================= 4 | 5 | JitLog is a `PyPy`_ logging facility that outputs information about compiler internals. 6 | It was built primarily for the following use cases: 7 | 8 | * Understand JIT internals and be able to browse emitted code (both IR operations and machine code) 9 | * Track down speed issues 10 | * Help bug reporting 11 | 12 | This version is now integrated within the webservice `vmprof.com`_ and can be used free of charge. 13 | 14 | Usage 15 | ===== 16 | 17 | The following commands show example usages:: 18 | 19 | # upload both vmprof & jitlog profiles 20 | pypy -m vmprof --web --jitlog 21 | 22 | # upload only a jitlog profile 23 | pypy -m jitlog --web 24 | 25 | # upload a jitlog when your program segfaults/crashes 26 | $ pypy -m jitlog -o /tmp/file.log 27 | 28 | $ pypy -m jitlog --upload /tmp/file.log 29 | 30 | .. _`vmprof.com`: http://vmprof.com 31 | .. _`PyPy`: http://pypy.org 32 | -------------------------------------------------------------------------------- /docs/native.rst: -------------------------------------------------------------------------------- 1 | Native profiling 2 | ================ 3 | 4 | Version 0.4+ is able to profile native functions (routines written in 5 | a different language like C) on Mac OS X and Linux. See below for a technical overview. 6 | 7 | By default this feature is enabled. To disable native profiling add ``--no-native`` 8 | as a command line switch. 9 | 10 | **NOTE** be sure to provide debugging symbols for your native functions, otherwise 11 | you will not see the symbol name of your e.g. C program. 12 | 13 | Technical Design 14 | ---------------- 15 | 16 | Native sampling utilizes ``libunwind`` in the signal handler to unwind the stack. 17 | 18 | Each stack frame is inspected until the frame evaluation function is encountered. Then the stack walking 19 | switches back to the traditional Python frame walking. Callbacks (Python frame -> ... C frame ... -> Python frame -> 20 | C frame) 21 | will not display intermediate native functions. It would give the impression that the first C frame was never called, 22 | but it will show the second C frame. 23 | 24 | Earlier Implementation 25 | ---------------------- 26 | 27 | Prior to 0.4.3 the following logic was implemented (see e.g. commit 3912330b509d). 28 | It was removed because it could not be implemented on Mac OS X 29 | (libunwind misses register/cancel functions for generated machine code). 30 | 31 | To find the corresponding ``PyFrameObject`` during stack unwinding vmprof inserts a trampoline on CPython (called ``vmprof_eval``) and places it just before ``PyEval_EvalFrameEx``. It is a callee trampoline saving the ``PyFrameObject`` in the callee saved register ``%rbx``. On Python 3.6+ the frame evaluation `PEP 523`_ is utilized as trampoline. 32 | 33 | .. _`PEP 523`: https://www.python.org/dev/peps/pep-0523/ 34 | 35 | -------------------------------------------------------------------------------- /docs/query.rst: -------------------------------------------------------------------------------- 1 | Jit Log Query Interface 2 | ======================= 3 | 4 | This command line interface can be used to pretty print optimized 5 | program parts. If you are unfamiliar with the JIT compiler 6 | built into PyPy, we highly recommend reading the `docs`_ for it. 7 | 8 | .. _`docs`: https://rpython.readthedocs.io/en/latest/jit/index.html 9 | 10 | Security Warning 11 | ---------------- 12 | 13 | It is discouraged to run the query API on a remote server. 14 | As soon as the query parameter (`-q`) is parameterized, arbitrary 15 | code execution can be performed. 16 | Note that this is fine as long one can trust the user. 17 | 18 | Basic Usage 19 | ----------- 20 | 21 | Let's go ahead and inspect the `example.py` program in this repository. 22 | It is assumed that the reader setup vmprof for pypy already (e.g. in a 23 | virtualenv). 24 | 25 | Now run the following command to generate the log:: 26 | 27 | # run your program and output the log 28 | pypy -m vmprof -o log.jit example.py 29 | 30 | This generates the file that normally is sent to `vmprof.com`_ whenever 31 | `--web` is provided. 32 | 33 | The query interface is a the flag '-q' which incooperates a small 34 | query language. Here is an example:: 35 | 36 | pypy -m jitlog log.jit -q 'bridges & op("int_add_ovf")' 37 | ... # will print the filtered traces 38 | 39 | .. _`vmprof.com`: http://vmprof.com 40 | 41 | Query API 42 | --------- 43 | 44 | Brand new. Subject to change! 45 | 46 | .. function:: loops 47 | 48 | Filter: Reduces the output to loops only 49 | 50 | .. function:: bridges 51 | 52 | Filter: Reduces the output to bridges only 53 | 54 | .. function:: func(name) 55 | 56 | Filter: Selects a trace if it happens to optimize the function containing the name. 57 | 58 | .. function:: op(name) 59 | 60 | Filter: Only selects a traces if it contains the IR operation name. 61 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | 2 | def test_1(): 3 | return [a for a in range(1000000)] 4 | 5 | 6 | def test_2(): 7 | return [b for b in range(10000000)] 8 | 9 | 10 | def main(): 11 | test_1() 12 | test_2() 13 | 14 | return test_1() + test_2() 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /jitlog/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from jitlog.upload import upload 3 | -------------------------------------------------------------------------------- /jitlog/__main__.py: -------------------------------------------------------------------------------- 1 | import runpy 2 | import sys, os 3 | import tempfile 4 | import argparse 5 | from jitlog.upload import upload as jitlog_upload 6 | from jitlog.parser import parse_jitlog 7 | from jitlog import query 8 | from vmshare.service import Service 9 | 10 | try: 11 | import _jitlog 12 | except ImportError: 13 | _jitlog = None 14 | 15 | def build_argparser(): 16 | # TODO merge with arguments of vmprof.cli 17 | parser = argparse.ArgumentParser( 18 | description='Jitlog', 19 | prog="jitlog" 20 | ) 21 | parser.add_argument( 22 | 'program', 23 | help='program' 24 | ) 25 | parser.add_argument( 26 | 'args', 27 | nargs=argparse.REMAINDER, 28 | help='program arguments' 29 | ) 30 | 31 | parser.add_argument('--query', '-q', dest='query', default=None, 32 | help='Select traces and pretty print them. ' \ 33 | 'The query API can be found on https://vmprof.readthedocs.org' 34 | ) 35 | parser.add_argument( 36 | '--web-auth', 37 | help='Authtoken for your acount on the server, works only when --web is used' 38 | ) 39 | 40 | parser.add_argument( 41 | '--web-url', 42 | metavar='url', 43 | default='http://vmprof.com', 44 | help='Provide URL instead of the default vmprof.com)' 45 | ) 46 | output_mode_args = parser.add_mutually_exclusive_group() 47 | output_mode_args.add_argument( 48 | '--web', 49 | action='store_true', 50 | help='Upload profiling stats to a remote server (defaults to vmprof.com)' 51 | ) 52 | output_mode_args.add_argument( 53 | '--output', '-o', 54 | metavar='file.prof', 55 | type=argparse.FileType('w+b'), 56 | help='Save profiling data to file' 57 | ) 58 | output_mode_args.add_argument( 59 | '--upload', 60 | action='store_true', 61 | help='Upload the file provided as the program argument' 62 | ) 63 | 64 | return parser 65 | 66 | def main(): 67 | parser = build_argparser() 68 | args = parser.parse_args(sys.argv[1:]) 69 | web = args.web 70 | 71 | if args.query is not None: 72 | from jitlog import prettyprinter as pp 73 | sys.stderr.write("Parsing jitlog...") 74 | sys.stderr.flush() 75 | forest = parse_jitlog(args.program) 76 | sys.stderr.write("done\n") 77 | query_str = args.query or "traces()" 78 | q = query.new_unsafe_query(query_str) 79 | objs = q(forest) 80 | color = True 81 | pp_clazz = pp.ColoredPrettyPrinter if color else pp.PrettyPrinter 82 | with pp_clazz() as ppinst: 83 | for trace in objs: 84 | ppinst.trace(sys.stdout, trace) 85 | if args.query is None: 86 | sys.stderr.write("-" * 10 + '\n') 87 | sys.stderr.write("Display the jitlog with an empty query (defaults to -q 'traces()'). " 88 | "Add -q 'your query' if you want to narrow down the output\n") 89 | sys.exit(0) 90 | 91 | if args.upload: 92 | host, auth = args.web_url, args.web_auth 93 | service = Service(host, auth) 94 | service.post({ Service.FILE_JIT_PROFILE: args.program }) 95 | sys.exit(0) 96 | 97 | if not _jitlog: 98 | if '__pypy__' in sys.builtin_module_names: 99 | sys.stderr.write("No _jitlog module. This PyPy version is too old!\n") 100 | else: 101 | sys.stderr.write("No _jitlog module. Use PyPy instead of CPython!\n") 102 | 103 | if not web: 104 | prof_file = args.output 105 | else: 106 | prof_file = tempfile.NamedTemporaryFile(delete=False) 107 | prof_name = prof_file.name 108 | 109 | 110 | fd = os.open(prof_name, os.O_WRONLY | os.O_TRUNC | os.O_CREAT) 111 | _jitlog.enable(fd) 112 | 113 | try: 114 | sys.argv = [args.program] + args.args 115 | sys.path.insert(0, os.path.dirname(args.program)) 116 | runpy.run_path(args.program, run_name='__main__') 117 | except BaseException as e: 118 | if not isinstance(e, (KeyboardInterrupt, SystemExit)): 119 | raise 120 | # not need to close fd, will be here 121 | _jitlog.disable() 122 | 123 | if web: 124 | forest = parse_jitlog(prof_name) 125 | if forest.extract_source_code_lines(): 126 | # only copy the tags if the jitlog has no source code yet! 127 | forest.copy_and_add_source_code_tags() 128 | host, auth = args.web_url, args.web_auth 129 | service = Service(host, auth) 130 | service.post({ Service.FILE_JIT_PROFILE: forest.filepath }) 131 | forest.unlink_jitlog() # free space! 132 | 133 | main() 134 | -------------------------------------------------------------------------------- /jitlog/constants.py: -------------------------------------------------------------------------------- 1 | # generated constants from rpython/rlib/jitlog.py 2 | import struct 3 | MARK_JITLOG_START = struct.pack("b", 0x10) 4 | MARK_INPUT_ARGS = struct.pack("b", 0x11) 5 | MARK_RESOP_META = struct.pack("b", 0x12) 6 | MARK_RESOP = struct.pack("b", 0x13) 7 | MARK_RESOP_DESCR = struct.pack("b", 0x14) 8 | MARK_ASM_ADDR = struct.pack("b", 0x15) 9 | MARK_ASM = struct.pack("b", 0x16) 10 | MARK_TRACE = struct.pack("b", 0x17) 11 | MARK_TRACE_OPT = struct.pack("b", 0x18) 12 | MARK_TRACE_ASM = struct.pack("b", 0x19) 13 | MARK_STITCH_BRIDGE = struct.pack("b", 0x1a) 14 | MARK_START_TRACE = struct.pack("b", 0x1b) 15 | MARK_JITLOG_COUNTER = struct.pack("b", 0x1c) 16 | MARK_INIT_MERGE_POINT = struct.pack("b", 0x1d) 17 | MARK_JITLOG_HEADER = struct.pack("b", 0x1e) 18 | MARK_MERGE_POINT = struct.pack("b", 0x1f) 19 | MARK_COMMON_PREFIX = struct.pack("b", 0x20) 20 | MARK_ABORT_TRACE = struct.pack("b", 0x21) 21 | MARK_SOURCE_CODE = struct.pack("b", 0x22) 22 | MARK_REDIRECT_ASSEMBLER = struct.pack("b", 0x23) 23 | MARK_TMP_CALLBACK = struct.pack("b", 0x24) 24 | MARK_JITLOG_END = struct.pack("b", 0x25) 25 | MP_INDEX = (0x4,"i") 26 | MP_SCOPE = (0x8,"s") 27 | MP_FILENAME = (0x1,"s") 28 | MP_OPCODE = (0x10,"s") 29 | MP_LINENO = (0x2,"i") 30 | MP_STR = (0x0,"s") 31 | MP_INT = (0x0,"i") 32 | SEM_TYPE_NAMES = { 33 | 0x4: "index", 34 | 0x8: "scope", 35 | 0x1: "filename", 36 | 0x10: "opcode", 37 | 0x2: "lineno", 38 | } 39 | -------------------------------------------------------------------------------- /jitlog/merge_point.py: -------------------------------------------------------------------------------- 1 | from jitlog import constants as const 2 | from vmshare.binary import read_string, read_le_u64 3 | 4 | class MergePointDecoder(object): 5 | def __init__(self, sem_type): 6 | self.sem_type = sem_type 7 | self.last_prefix = None 8 | 9 | def set_prefix(self, prefix): 10 | self.last_prefix = prefix 11 | 12 | class IntMergePointDecoder(MergePointDecoder): 13 | def decode(self, fileobj): 14 | assert fileobj.read(1) == b'\x00' 15 | return read_le_u64(fileobj) 16 | 17 | class StrMergePointDecoder(MergePointDecoder): 18 | def decode(self, fileobj): 19 | type = fileobj.read(1) 20 | if type == b'\xef': 21 | return self.last_prefix[:] 22 | string = read_string(fileobj, True) 23 | if type == b'\x00': 24 | return self.last_prefix + string 25 | else: 26 | assert type == b'\xff' 27 | return string 28 | 29 | def get_decoder(sem_type, gen_type, version): 30 | assert 0 <= sem_type <= const.MP_OPCODE[0] 31 | if gen_type == "s": 32 | return StrMergePointDecoder(sem_type) 33 | else: 34 | assert gen_type == "i" 35 | return IntMergePointDecoder(sem_type) 36 | 37 | -------------------------------------------------------------------------------- /jitlog/parser.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import traceback 4 | from jitlog import constants as const, marks 5 | from jitlog.objects import TraceForest 6 | from vmshare.binary import read_string 7 | from io import BytesIO 8 | from collections import defaultdict 9 | 10 | JITLOG_MIN_VERSION = 1 11 | JITLOG_VERSION = 1 12 | 13 | class ParseContext(object): 14 | def __init__(self, forest): 15 | self.descrs = defaultdict(list) 16 | self.forest = forest 17 | self.word_size = self.forest.word_size 18 | 19 | def read_le_addr(self, fileobj): 20 | b = fileobj.read(self.word_size) 21 | if self.word_size == 4: 22 | res = int(struct.unpack('I', b)[0]) 23 | else: 24 | res = int(struct.unpack('Q', b)[0]) 25 | return res 26 | 27 | 28 | class ParseException(Exception): 29 | pass 30 | 31 | def read_jitlog_data(filename): 32 | with open(str(filename), 'rb') as fileobj: 33 | return fileobj.read() 34 | 35 | def parse_jitlog(filename, data=None): 36 | if data is None: 37 | data = read_jitlog_data(filename) 38 | fileobj = BytesIO(data) 39 | f = _parse_jitlog(fileobj) 40 | f.filepath = filename 41 | return f 42 | 43 | def _parse_jitlog(fileobj): 44 | """ JitLog is parsed. if an exception is raised after 45 | the header has been read, it is saved in the field 'exc' on 46 | the forest returned. 47 | 48 | This allows to parse an incomplete log file an still display 49 | everything that was readable! 50 | If a ParseException is directly raised, this is an error that cannot 51 | be recovered from! 52 | """ 53 | try: 54 | is_jit_log = fileobj.read(1) == const.MARK_JITLOG_HEADER 55 | version = ord(fileobj.read(1)) | (ord(fileobj.read(1)) << 8) 56 | is_32bit = ord(fileobj.read(1)) 57 | machine = read_string(fileobj, True) 58 | forest = TraceForest(version, is_32bit, machine) 59 | ctx = ParseContext(forest) 60 | except Exception: 61 | raise ParseException("Header malformed") 62 | # 63 | if not is_jit_log: 64 | raise ParseException("Missing header. Provided input might not be a jitlog!") 65 | if version < JITLOG_MIN_VERSION: 66 | raise ParseException("Version %d is not supported" % version) 67 | while True: 68 | marker = fileobj.read(1) 69 | if len(marker) == 0: 70 | break # end of file! 71 | if not forest.is_jitlog_marker(marker): 72 | msg = "marker unknown: 0x%x at pos 0x%x" % \ 73 | (ord(marker), fileobj.tell()) 74 | forest.exc = ParseException(msg) 75 | break 76 | trace = forest.last_trace 77 | try: 78 | read = marks.get_reader(version, marker) 79 | read(ctx, trace, fileobj) 80 | forest.time_tick() 81 | except KeyError as e: 82 | forest.exc = e 83 | break 84 | except ParseException as e: 85 | forest.exc = e 86 | break 87 | except Exception as e: 88 | exc_type, exc_value, exc_traceback = sys.exc_info() 89 | tb = traceback.extract_tb(exc_traceback, limit=3) 90 | msg = "failed at 0x%x with marker %s with exc \"%s\". trace back: \"%s\"" %\ 91 | (fileobj.tell(), marker, str(e), tb) 92 | forest.exc = ParseException(msg) 93 | break 94 | 95 | return forest 96 | 97 | -------------------------------------------------------------------------------- /jitlog/prettyprinter.py: -------------------------------------------------------------------------------- 1 | from colorama import init, deinit, Fore, Back, Style 2 | 3 | class PrettyPrinter(object): 4 | def __enter__(self): 5 | pass 6 | 7 | def __exit__(self): 8 | pass 9 | 10 | def op(self, op): 11 | suffix = '' 12 | if op.result is not None and op.result != '?': 13 | suffix = "%s = " % self.var(op.result) 14 | descr = op.descr 15 | if descr is None: 16 | descr = '' 17 | else: 18 | descr = ', @' + self.descr(descr) 19 | args = [self.var(arg) for arg in op.args] 20 | return '%s%s(%s%s)' % (suffix, self.opname(op.opname), 21 | ', '.join(args), descr) 22 | 23 | def trace(self, fd, trace): 24 | for name, stage in trace.stages.items(): 25 | fd.write(self.stage_name(stage)) 26 | fd.write("\n") 27 | for op in stage.getoperations(): 28 | fd.write(' ' + self.op(op) + '\n') 29 | 30 | def var(self, var): 31 | return var 32 | 33 | def descr(self, descr): 34 | return descr 35 | 36 | def opname(self, opname): 37 | return opname 38 | 39 | def stage_name(self, stage): 40 | return str(stage) 41 | 42 | class ColoredPrettyPrinter(PrettyPrinter): 43 | def __enter__(self): 44 | init() 45 | return self 46 | 47 | def __exit__(self, a, b, c): 48 | deinit() 49 | 50 | def opname(self, opname): 51 | return Fore.CYAN + opname + Style.RESET_ALL 52 | 53 | def var(self, var): 54 | if len(var) > 0: 55 | if var[0] == 'i': 56 | return Fore.BLUE + var + Style.RESET_ALL 57 | if var[0] == 'p' or var[0] == 'r': 58 | return Fore.GREEN + var + Style.RESET_ALL 59 | if var[0] == 'f': 60 | return Fore.BLUE + var + Style.RESET_ALL 61 | return Fore.RED + var + Style.RESET_ALL 62 | 63 | -------------------------------------------------------------------------------- /jitlog/query.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | 4 | 5 | class Filter(object): 6 | def __and__(self, o): 7 | assert isinstance(o, Filter) 8 | return AndFilter(self, o) 9 | 10 | def __or__(self, o): 11 | assert isinstance(o, Filter) 12 | return OrFilter(self, o) 13 | 14 | def _filter(self, trace): 15 | return True 16 | 17 | class BinaryFilter(Filter): 18 | def __init__(self, left, right): 19 | self.left = left 20 | self.right = right 21 | 22 | class AndFilter(BinaryFilter): 23 | def _filter(self, trace): 24 | return self.left._filter(trace) and self.right._filter(trace) 25 | 26 | class OrFilter(BinaryFilter): 27 | def _filter(self, trace): 28 | if self.left._filter(trace): 29 | return True 30 | if self.right._filter(trace): 31 | return True 32 | return False 33 | 34 | class OpFilter(Filter): 35 | def __init__(self, name): 36 | self.name = name 37 | 38 | def _filter(self, trace): 39 | name = self.name 40 | for stage in trace.stages.keys(): 41 | for op in trace.get_stage(stage).get_ops(): 42 | if name in op.opname: 43 | return True 44 | return False 45 | 46 | class FuncFilter(Filter): 47 | def __init__(self, name): 48 | self.name = name 49 | 50 | def _filter(self, trace): 51 | name = self.name 52 | stage = trace.get_stage('noopt') 53 | if not stage: 54 | return False 55 | 56 | for mp in stage.get_merge_points(): 57 | if name in mp.get_scope(): 58 | return True 59 | 60 | return False 61 | 62 | class LoopFilter(Filter): 63 | def _filter(self, trace): 64 | return trace.type == 'loop' 65 | 66 | class BridgeFilter(Filter): 67 | def _filter(self, trace): 68 | return trace.type == 'bridge' 69 | 70 | loops = LoopFilter() 71 | 72 | bridges = BridgeFilter() 73 | 74 | 75 | QUERY_API = { 76 | # functions 77 | 'op': OpFilter, 78 | 'func': FuncFilter, 79 | # variable filters without state 80 | 'loops': loops, 81 | 'bridges': bridges, 82 | } 83 | 84 | 85 | class Query(object): 86 | def __init__(self, text): 87 | self.query_str = text 88 | self.forest = None 89 | 90 | def __call__(self, forest): 91 | self.forest = forest 92 | return self.evaluate(forest, self.query_str) 93 | 94 | def evaluate(self, forest, qstr): 95 | if qstr is None or qstr.strip() == '': 96 | return None 97 | 98 | api = {} 99 | for k,f in QUERY_API.items(): 100 | api[k] = f 101 | # --------------------------------------------- 102 | # SECURITY ISSUE: 103 | # never execute this in the server environment 104 | # --------------------------------------------- 105 | f = eval(qstr, {}, api) 106 | return [t for t in forest.traces.values() if f._filter(t)] 107 | 108 | def new_unsafe_query(query): 109 | return Query(query) 110 | 111 | 112 | -------------------------------------------------------------------------------- /jitlog/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/jitlog/test/__init__.py -------------------------------------------------------------------------------- /jitlog/test/data/code.py: -------------------------------------------------------------------------------- 1 | def add(a + b): # this should be line 1 2 | return a + b 3 | 4 | def mul(a): # 4 5 | c = a * 2 6 | d = c * 3 7 | return d + 5 8 | -------------------------------------------------------------------------------- /jitlog/test/data/code2.py: -------------------------------------------------------------------------------- 1 | class Name(object): # must be line 1 2 | def __init__(self): # note the following line has indent 7 3 | self.unique = False 4 | -------------------------------------------------------------------------------- /jitlog/test/test_encoding.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import sys 3 | import pytest 4 | from jitlog.objects import (TraceForest, MergePoint, FlatOp) 5 | from jitlog import constants as const 6 | 7 | PY3 = sys.version_info[0] >= 3 8 | 9 | @pytest.mark.parametrize('encoding,text,decoded,bom', 10 | [('ascii', b'a!1%$', u'a!1%$', None), 11 | ('utf-8', b"\x41\xE2\x89\xA2\xCE\x91\x2E", u'A≢Α.', None), 12 | ('latin-1', b'\xDCber', u'Über', None), 13 | ]) 14 | def test_merge_point_extract_source_code(encoding,text,decoded,bom,tmpdir): 15 | forest = TraceForest(1) 16 | trace = forest.add_trace('loop', 0, 0) 17 | trace.start_mark(const.MARK_TRACE_OPT) 18 | file = tmpdir.join("file"+encoding+".py") 19 | l = [] 20 | if bom: 21 | l.append(bom) 22 | l.append("# coding: ".encode(encoding)) 23 | l.append(encoding.encode(encoding)) 24 | l.append("\r\n".encode(encoding)) 25 | l.append("print(\"".encode(encoding)) 26 | l.append(text) 27 | l.append("\")".encode(encoding)) 28 | l.append("\r\n".encode(encoding)) 29 | file.write_binary(b''.join(l)) 30 | trace.add_instr(MergePoint({0x1: str(file), 0x2: 2})) 31 | forest.extract_source_code_lines() 32 | line = forest.source_lines[str(file)][2] 33 | if PY3: 34 | assert line == (0, "print(\"" + decoded + "\")") 35 | else: 36 | assert line == (0, "print(\"" + text + "\")") 37 | -------------------------------------------------------------------------------- /jitlog/test/test_query.py: -------------------------------------------------------------------------------- 1 | from jitlog.objects import (TraceForest, FlatOp, MergePoint) 2 | from jitlog import constants as c 3 | from jitlog import query 4 | 5 | class TestQueries(object): 6 | def q(self, forest, text): 7 | return query.new_unsafe_query(text)(forest) 8 | 9 | def test_query_empty_forest(self): 10 | f = TraceForest(3, is_32bit=False, machine='s390x') 11 | assert self.q(f, '') == None 12 | assert self.q(f, 'loops & bridges') == [] 13 | 14 | def test_query_small_forest(self): 15 | f = TraceForest(3, is_32bit=False, machine='s390x') 16 | # 17 | t = f.add_trace('loop', 0, 0, 'jd') 18 | stage = t.start_mark(c.MARK_TRACE_OPT) 19 | stage.append_op(FlatOp(0, 'load', [], '?')) 20 | # 21 | t2 = f.add_trace('loop', 1, 1, 'jd') 22 | stage = t2.start_mark(c.MARK_TRACE_OPT) 23 | stage.append_op(FlatOp(0, 'store', [], '?')) 24 | # 25 | assert len(f.traces) == 2 26 | assert self.q(f, '') == None 27 | assert self.q(f, 'op("load")') == [t] 28 | 29 | def test_query_loops_and_bridges(self): 30 | f = TraceForest(3, is_32bit=False, machine='s390x') 31 | # 32 | t = f.add_trace('loop', 0, 0, 'jd') 33 | t2 = f.add_trace('bridge', 1, 1, 'jd') 34 | # 35 | assert len(f.traces) == 2 36 | assert self.q(f, 'loops') == [t] 37 | assert self.q(f, 'bridges') == [t2] 38 | 39 | def test_filter(self): 40 | from jitlog.query import loops, bridges, Filter 41 | f = TraceForest(3, is_32bit=False, machine='s390x') 42 | loop = f.add_trace('loop', 0, 0, 'su') 43 | bridge = f.add_trace('bridge', 1, 1, 'shi') 44 | assert loops._filter(loop) 45 | assert not loops._filter(bridge) 46 | assert not bridges._filter(loop) 47 | assert bridges._filter(bridge) 48 | r = loops | bridges 49 | assert isinstance(r, Filter) 50 | assert r._filter(loop) 51 | assert r._filter(bridge) 52 | 53 | def test_func_filter(self): 54 | from jitlog.query import FuncFilter as func 55 | f = TraceForest(3, is_32bit=False, machine='s390x') 56 | loop = f.add_trace('loop', 0, 0, 'su') 57 | stage = loop.start_mark(c.MARK_TRACE) 58 | stage.append_op(MergePoint({ c.MP_SCOPE[0]: '_sake_in_a_glass' })) 59 | assert not func('hello')._filter(loop) 60 | assert func('sake')._filter(loop) 61 | assert not func('s_a_k_e')._filter(loop) 62 | 63 | -------------------------------------------------------------------------------- /jitlog/upload.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import gzip 4 | import tempfile 5 | import requests 6 | try: 7 | from urlparse import urlparse 8 | except ImportError: 9 | # python 3 10 | from urllib.parse import urlparse 11 | 12 | def compress_file(filename): 13 | fileno, name = tempfile.mkstemp(prefix='jit', suffix='.log.zip') 14 | os.close(fileno) 15 | with open(filename, 'rb') as fd: 16 | with gzip.open(name, 'wb') as zipfd: 17 | while True: 18 | chunk = fd.read(1024) 19 | if not chunk: 20 | break 21 | zipfd.write(chunk) 22 | return name 23 | 24 | def upload(filepath, url): 25 | zfilepath = compress_file(filepath) 26 | with open(zfilepath, 'rb') as fd: 27 | r = requests.post(url, files={ 'file': fd }) 28 | if r.status_code != 200: 29 | sys.stderr.write("PyPy JIT log: Server rejected file. status: %d, msg: '%s'\n" % \ 30 | (r.status_code,r.text)) 31 | return 32 | checksum = r.text[1:-1] 33 | assert checksum != "" 34 | netloc = urlparse(url).netloc 35 | sys.stderr.write("PyPy JIT log: http://%s/#/%s/traces\n" % (netloc, checksum)) 36 | 37 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | 2 | [pytest] 3 | testpaths = jitlog vmprof 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages, Extension 2 | from distutils.command.build_py import build_py 3 | import os, sys 4 | import subprocess 5 | import platform 6 | 7 | IS_PYPY = '__pypy__' in sys.builtin_module_names 8 | 9 | class vmprof_build(build_py, object): 10 | def run(self): 11 | super(vmprof_build, self).run() 12 | 13 | BASEDIR = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | def _supported_unix(): 16 | if sys.platform.startswith('linux'): 17 | return 'linux' 18 | if sys.platform.startswith('freebsd'): 19 | return 'bsd' 20 | return False 21 | 22 | if IS_PYPY: 23 | ext_modules = [] # built-in 24 | else: 25 | extra_compile_args = [] 26 | extra_source_files = [ 27 | 'src/symboltable.c', 28 | ] 29 | if sys.platform == 'win32': 30 | extra_source_files = [ 31 | 'src/vmprof_win.c', 32 | ] # remove the native source files 33 | libraries = [] 34 | extra_compile_args = ['-DVMPROF_WINDOWS=1'] 35 | elif sys.platform == 'darwin': 36 | libraries = [] 37 | extra_compile_args = ['-Wno-unused'] 38 | extra_compile_args += ['-DVMPROF_APPLE=1'] 39 | extra_compile_args += ['-DVMPROF_UNIX=1'] 40 | # overwrite the optimization level, if it is not optimized enough, 41 | # it might use the regiter rbx... 42 | extra_compile_args += ['-g'] 43 | extra_compile_args += ['-O2'] 44 | extra_source_files += ['src/vmprof_unix.c', 'src/vmprof_mt.c'] 45 | elif _supported_unix(): 46 | libraries = ['dl','unwind'] 47 | extra_compile_args = ['-Wno-unused'] 48 | if _supported_unix() == 'linux': 49 | extra_compile_args += ['-DVMPROF_LINUX=1'] 50 | if _supported_unix() == 'bsd': 51 | libraries = ['unwind'] 52 | extra_compile_args += ['-DVMPROF_BSD=1'] 53 | extra_compile_args += ['-I/usr/local/include'] 54 | extra_compile_args += ['-DVMPROF_UNIX=1'] 55 | extra_source_files += [ 56 | 'src/vmprof_mt.c', 57 | 'src/vmprof_unix.c', 58 | 'src/libbacktrace/backtrace.c', 59 | 'src/libbacktrace/state.c', 60 | 'src/libbacktrace/elf.c', 61 | 'src/libbacktrace/dwarf.c', 62 | 'src/libbacktrace/fileline.c', 63 | 'src/libbacktrace/mmap.c', 64 | 'src/libbacktrace/mmapio.c', 65 | 'src/libbacktrace/posix.c', 66 | 'src/libbacktrace/sort.c', 67 | ] 68 | # configure libbacktrace!! 69 | class vmprof_build(build_py, object): 70 | def run(self): 71 | orig_dir = os.getcwd() 72 | os.chdir(os.path.join(BASEDIR, "src", "libbacktrace")) 73 | subprocess.check_call(["./configure"]) 74 | os.chdir(orig_dir) 75 | super(vmprof_build, self).run() 76 | 77 | else: 78 | raise NotImplementedError("platform '%s' is not supported!" % sys.platform) 79 | extra_compile_args.append('-I src/') 80 | extra_compile_args.append('-I src/libbacktrace') 81 | if sys.version_info[:2] == (3,11): 82 | extra_source_files += ['src/populate_frames.c'] 83 | ext_modules = [Extension('_vmprof', 84 | sources=[ 85 | 'src/_vmprof.c', 86 | 'src/machine.c', 87 | 'src/compat.c', 88 | 'src/vmp_stack.c', 89 | 'src/vmprof_common.c', 90 | 'src/vmprof_memory.c', 91 | ] + extra_source_files, 92 | depends=[ 93 | 'src/vmprof_unix.h', 94 | 'src/vmprof_mt.h', 95 | 'src/vmprof_common.h', 96 | 'src/vmp_stack.h', 97 | 'src/symboltable.h', 98 | 'src/machine.h', 99 | 'src/vmprof.h', 100 | 'src/vmprof_memory.h', 101 | ], 102 | extra_compile_args=extra_compile_args, 103 | libraries=libraries)] 104 | 105 | if sys.version_info[:2] >= (3, 3): 106 | extra_install_requires = [] 107 | else: 108 | extra_install_requires = ["backports.shutil_which"] 109 | 110 | setup( 111 | name='vmprof', 112 | author='vmprof team', 113 | author_email='fijal@baroquesoftware.com', 114 | version="0.4.18.1", 115 | packages=find_packages(), 116 | description="Python's vmprof client", 117 | long_description='See https://vmprof.readthedocs.org/', 118 | url='https://github.com/vmprof/vmprof-python', 119 | cmdclass={'build_py': vmprof_build}, 120 | install_requires=[ 121 | 'requests', 122 | 'six', 123 | 'pytz', 124 | 'colorama', 125 | ] + extra_install_requires, 126 | python_requires='<3.12', 127 | tests_require=['pytest','cffi','hypothesis'], 128 | entry_points = { 129 | 'console_scripts': [ 130 | 'vmprofshow = vmprof.show:main' 131 | ]}, 132 | classifiers=[ 133 | 'License :: OSI Approved :: MIT License', 134 | 'Programming Language :: Python', 135 | 'Programming Language :: Python :: 2.7', 136 | 'Programming Language :: Python :: 3', 137 | 'Programming Language :: Python :: Implementation :: CPython', 138 | 'Programming Language :: Python :: Implementation :: PyPy', 139 | ], 140 | zip_safe=False, 141 | include_package_data=True, 142 | ext_modules=ext_modules, 143 | ) 144 | -------------------------------------------------------------------------------- /src/_vmprof.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmprof.h" 4 | 5 | #ifdef VMPROF_WINDOWS 6 | 7 | #include 8 | // CPython 3.6 defines all the inttypes for us, we do not need the msiinttypes 9 | // library for that version or any newer! 10 | #if (PY_VERSION_HEX < 0x3060000) 11 | #include "msiinttypes/inttypes.h" 12 | #include "msiinttypes/stdint.h" 13 | #endif 14 | 15 | #else 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | /** 22 | * This whole setup is very strange. There was just one C file called 23 | * _vmprof.c which included all *.h files to copy code. Unsure what 24 | * the goal was with this design, but I assume it just 'GREW' 25 | * 26 | * Thus I'm (plan_rich) slowly trying to separate this. *.h files 27 | * should not have complex implementations (all of them currently have them) 28 | */ 29 | 30 | 31 | #define SINGLE_BUF_SIZE (8192 - 2 * sizeof(unsigned int)) 32 | 33 | #define ROUTINE_IS_PYTHON(RIP) ((unsigned long long)RIP & 0x1) == 0 34 | #define ROUTINE_IS_C(RIP) ((unsigned long long)RIP & 0x1) == 1 35 | 36 | /* This returns the address of the code object 37 | as the identifier. The mapping from identifiers to string 38 | representations of the code object is done elsewhere, namely: 39 | 40 | * If the code object dies while vmprof is enabled, 41 | PyCode_Type.tp_dealloc will emit it. (We don't handle nicely 42 | for now the case where several code objects are created and die 43 | at the same memory address.) 44 | 45 | * When _vmprof.disable() is called, then we look around the 46 | process for code objects and emit all the ones that we can 47 | find (which we hope is very close to 100% of them). 48 | */ 49 | #define CODE_ADDR_TO_UID(co) (((intptr_t)(co))) 50 | 51 | #define CPYTHON_HAS_FRAME_EVALUATION PY_VERSION_HEX >= 0x30600B0 52 | 53 | int vmp_write_all(const char *buf, size_t bufsize); 54 | -------------------------------------------------------------------------------- /src/compat.c: -------------------------------------------------------------------------------- 1 | #include "compat.h" 2 | 3 | #include 4 | #include 5 | #if VMPROF_WINDOWS 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #else 9 | #include 10 | #include 11 | #endif 12 | 13 | static int _vmp_profile_fileno = -1; 14 | 15 | int vmp_profile_fileno(void) { 16 | return _vmp_profile_fileno; 17 | } 18 | void vmp_set_profile_fileno(int fileno) { 19 | _vmp_profile_fileno = fileno; 20 | } 21 | 22 | #ifndef VMPROF_WINDOWS 23 | int vmp_write_all(const char *buf, size_t bufsize) 24 | { 25 | ssize_t count; 26 | if (_vmp_profile_fileno == -1) { 27 | return -1; 28 | } 29 | while (bufsize > 0) { 30 | count = write(_vmp_profile_fileno, buf, bufsize); 31 | if (count <= 0) 32 | return -1; /* failed */ 33 | buf += count; 34 | bufsize -= count; 35 | } 36 | return 0; 37 | } 38 | #endif 39 | 40 | int vmp_write_meta(const char * key, const char * value) 41 | { 42 | char marker = MARKER_META; 43 | long x = (long)strlen(key); 44 | vmp_write_all(&marker, 1); 45 | vmp_write_all((char*)&x, sizeof(long)); 46 | vmp_write_all(key, x); 47 | x = (long)strlen(value); 48 | vmp_write_all((char*)&x, sizeof(long)); 49 | vmp_write_all(value, x); 50 | return 0; 51 | } 52 | 53 | /** 54 | * Write the time and zone now. 55 | */ 56 | 57 | struct timezone_buf { 58 | int64_t tv_sec; 59 | int64_t tv_usec; 60 | }; 61 | #define __SIZE (1+sizeof(struct timezone_buf)+8) 62 | 63 | #ifdef VMPROF_UNIX 64 | int vmp_write_time_now(int marker) { 65 | char buffer[__SIZE]; 66 | struct timezone_buf buf; 67 | 68 | (void)memset(&buffer, 0, __SIZE); 69 | 70 | assert((marker == MARKER_TRAILER || marker == MARKER_TIME_N_ZONE) && \ 71 | "marker must be either a trailer or time_n_zone!"); 72 | 73 | struct timeval tv; 74 | time_t now; 75 | struct tm tm; 76 | 77 | 78 | /* copy over to the struct */ 79 | if (gettimeofday(&tv, NULL) != 0) { 80 | return -1; 81 | } 82 | if (time(&now) == (time_t)-1) { 83 | return -1; 84 | } 85 | if (localtime_r(&now, &tm) == NULL) { 86 | return -1; 87 | } 88 | buf.tv_sec = tv.tv_sec; 89 | buf.tv_usec = tv.tv_usec; 90 | // IF we really support time zones: 91 | // use a cross platform datetime library that outputs iso8601 strings 92 | // strncpy(((char*)buffer)+__SIZE-8, tm.tm_zone, 8); 93 | 94 | buffer[0] = marker; 95 | (void)memcpy(buffer+1, &buf, sizeof(struct timezone_buf)); 96 | vmp_write_all(buffer, __SIZE); 97 | return 0; 98 | } 99 | #endif 100 | 101 | #ifdef VMPROF_WINDOWS 102 | int vmp_write_time_now(int marker) { 103 | char buffer[__SIZE]; 104 | struct timezone_buf buf; 105 | 106 | /** 107 | * http://stackoverflow.com/questions/10905892/equivalent-of-gettimeday-for-windows 108 | */ 109 | 110 | // Note: some broken versions only have 8 trailing zero's, the correct 111 | // epoch has 9 trailing zero's 112 | static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); 113 | 114 | SYSTEMTIME system_time; 115 | FILETIME file_time; 116 | uint64_t time; 117 | 118 | (void)memset(&buffer, 0, __SIZE); 119 | 120 | assert((marker == MARKER_TRAILER || marker == MARKER_TIME_N_ZONE) && \ 121 | "marker must be either a trailer or time_n_zone!"); 122 | 123 | 124 | GetSystemTime( &system_time ); 125 | SystemTimeToFileTime( &system_time, &file_time ); 126 | time = ((uint64_t)file_time.dwLowDateTime ) ; 127 | time += ((uint64_t)file_time.dwHighDateTime) << 32; 128 | 129 | buf.tv_sec = ((time - EPOCH) / 10000000L); 130 | buf.tv_usec = (system_time.wMilliseconds * 1000); 131 | 132 | // time zone not implemented on windows 133 | // IF we really support time zones: 134 | // use a cross platform datetime library that outputs iso8601 strings 135 | memset(((char*)buffer)+__SIZE-8, 0, 8); 136 | 137 | buffer[0] = marker; 138 | (void)memcpy(buffer+1, &buf, sizeof(struct timezone_buf)); 139 | vmp_write_all(buffer, __SIZE); 140 | return 0; 141 | } 142 | #endif 143 | #undef __SIZE 144 | -------------------------------------------------------------------------------- /src/compat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmprof.h" 4 | 5 | #ifndef RPYTHON_VMPROF 6 | # if PY_MAJOR_VERSION >= 3 7 | #define PyStr_AS_STRING PyBytes_AS_STRING 8 | #define PyStr_GET_SIZE PyBytes_GET_SIZE 9 | #define PyStr_NEW PyUnicode_FromString 10 | #define PyStr_n_NEW PyUnicode_FromStringAndSize 11 | #define PyLong_NEW PyLong_FromSsize_t 12 | # else 13 | #define PyStr_AS_STRING PyString_AS_STRING 14 | #define PyStr_GET_SIZE PyString_GET_SIZE 15 | #define PyStr_NEW PyString_FromString 16 | #define PyStr_n_NEW PyString_FromStringAndSize 17 | #define PyLong_NEW PyInt_FromSsize_t 18 | #define PyLong_AsLong PyInt_AsLong 19 | # endif 20 | #endif 21 | 22 | int vmp_write_all(const char *buf, size_t bufsize); 23 | int vmp_write_time_now(int marker); 24 | int vmp_write_meta(const char * key, const char * value); 25 | 26 | int vmp_profile_fileno(void); 27 | void vmp_set_profile_fileno(int fileno); 28 | -------------------------------------------------------------------------------- /src/libbacktrace/ChangeLog.jit: -------------------------------------------------------------------------------- 1 | 2014-09-24 David Malcolm 2 | 3 | * ChangeLog.jit: Add copyright footer. 4 | 5 | 2013-10-03 David Malcolm 6 | 7 | * configure.ac: Add --enable-host-shared. 8 | * configure: Regenerate. 9 | 10 | Copyright (C) 2013-2014 Free Software Foundation, Inc. 11 | 12 | Copying and distribution of this file, with or without modification, 13 | are permitted in any medium without royalty provided the copyright 14 | notice and this notice are preserved. 15 | -------------------------------------------------------------------------------- /src/libbacktrace/Makefile.am: -------------------------------------------------------------------------------- 1 | # Makefile.am -- Backtrace Makefile. 2 | # Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | 8 | # (1) Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | 11 | # (2) Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in 13 | # the documentation and/or other materials provided with the 14 | # distribution. 15 | 16 | # (3) The name of the author may not be used to 17 | # endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | # POSSIBILITY OF SUCH DAMAGE. 31 | 32 | ACLOCAL_AMFLAGS = -I .. -I ../config 33 | 34 | AM_CPPFLAGS = -I $(top_srcdir)/../include -I $(top_srcdir)/../libgcc \ 35 | -I ../libgcc 36 | 37 | AM_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) $(PIC_FLAG) 38 | 39 | noinst_LTLIBRARIES = libbacktrace.la 40 | 41 | libbacktrace_la_SOURCES = \ 42 | backtrace.h \ 43 | atomic.c \ 44 | dwarf.c \ 45 | fileline.c \ 46 | internal.h \ 47 | posix.c \ 48 | print.c \ 49 | sort.c \ 50 | state.c 51 | 52 | BACKTRACE_FILES = \ 53 | backtrace.c \ 54 | simple.c \ 55 | nounwind.c 56 | 57 | FORMAT_FILES = \ 58 | elf.c \ 59 | pecoff.c \ 60 | unknown.c 61 | 62 | VIEW_FILES = \ 63 | read.c \ 64 | mmapio.c 65 | 66 | ALLOC_FILES = \ 67 | alloc.c \ 68 | mmap.c 69 | 70 | EXTRA_libbacktrace_la_SOURCES = \ 71 | $(BACKTRACE_FILES) \ 72 | $(FORMAT_FILES) \ 73 | $(VIEW_FILES) \ 74 | $(ALLOC_FILES) 75 | 76 | libbacktrace_la_LIBADD = \ 77 | $(BACKTRACE_FILE) \ 78 | $(FORMAT_FILE) \ 79 | $(VIEW_FILE) \ 80 | $(ALLOC_FILE) 81 | 82 | libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) 83 | 84 | # Testsuite. 85 | 86 | check_PROGRAMS = 87 | 88 | TESTS = $(check_PROGRAMS) 89 | 90 | if NATIVE 91 | 92 | btest_SOURCES = btest.c 93 | btest_CFLAGS = $(AM_CFLAGS) -g -O 94 | btest_LDADD = libbacktrace.la 95 | 96 | check_PROGRAMS += btest 97 | 98 | stest_SOURCES = stest.c 99 | stest_LDADD = libbacktrace.la 100 | 101 | check_PROGRAMS += stest 102 | 103 | endif NATIVE 104 | 105 | # We can't use automake's automatic dependency tracking, because it 106 | # breaks when using bootstrap-lean. Automatic dependency tracking 107 | # with GCC bootstrap will cause some of the objects to depend on 108 | # header files in prev-gcc/include, e.g., stddef.h and stdarg.h. When 109 | # using bootstrap-lean, prev-gcc is removed after each stage. When 110 | # running "make install", those header files will be gone, causing the 111 | # library to be rebuilt at install time. That may not succeed. 112 | 113 | # These manual dependencies do not include dependencies on unwind.h, 114 | # even though that is part of GCC, because where to find it depends on 115 | # whether we are being built as a host library or a target library. 116 | 117 | INCDIR = $(top_srcdir)/../include 118 | alloc.lo: config.h backtrace.h internal.h 119 | backtrace.lo: config.h backtrace.h internal.h 120 | btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h 121 | dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \ 122 | $(INCDIR)/filenames.h backtrace.h internal.h 123 | elf.lo: config.h backtrace.h internal.h 124 | fileline.lo: config.h backtrace.h internal.h 125 | mmap.lo: config.h backtrace.h internal.h 126 | mmapio.lo: config.h backtrace.h internal.h 127 | nounwind.lo: config.h internal.h 128 | pecoff.lo: config.h backtrace.h internal.h 129 | posix.lo: config.h backtrace.h internal.h 130 | print.lo: config.h backtrace.h internal.h 131 | read.lo: config.h backtrace.h internal.h 132 | simple.lo: config.h backtrace.h internal.h 133 | sort.lo: config.h backtrace.h internal.h 134 | stest.lo: config.h backtrace.h internal.h 135 | state.lo: config.h backtrace.h backtrace-supported.h internal.h 136 | unknown.lo: config.h backtrace.h internal.h 137 | -------------------------------------------------------------------------------- /src/libbacktrace/README: -------------------------------------------------------------------------------- 1 | The libbacktrace library 2 | Initially written by Ian Lance Taylor 3 | 4 | The libbacktrace library may be linked into a program or library and 5 | used to produce symbolic backtraces. Sample uses would be to print a 6 | detailed backtrace when an error occurs or to gather detailed 7 | profiling information. 8 | 9 | The libbacktrace library is provided under a BSD license. See the 10 | source files for the exact license text. 11 | 12 | The public functions are declared and documented in the header file 13 | backtrace.h, which should be #include'd by a user of the library. 14 | 15 | Building libbacktrace will generate a file backtrace-supported.h, 16 | which a user of the library may use to determine whether backtraces 17 | will work. See the source file backtrace-supported.h.in for the 18 | macros that it defines. 19 | 20 | As of September 2012, libbacktrace only supports ELF executables with 21 | DWARF debugging information. The library is written to make it 22 | straightforward to add support for other object file and debugging 23 | formats. 24 | -------------------------------------------------------------------------------- /src/libbacktrace/alloc.c: -------------------------------------------------------------------------------- 1 | /* alloc.c -- Memory allocation without mmap. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "backtrace.h" 40 | #include "internal.h" 41 | 42 | /* Allocation routines to use on systems that do not support anonymous 43 | mmap. This implementation just uses malloc, which means that the 44 | backtrace functions may not be safely invoked from a signal 45 | handler. */ 46 | 47 | /* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't 48 | report an error. */ 49 | 50 | void * 51 | backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED, 52 | size_t size, backtrace_error_callback error_callback, 53 | void *data) 54 | { 55 | void *ret; 56 | 57 | ret = malloc (size); 58 | if (ret == NULL) 59 | { 60 | if (error_callback) 61 | error_callback (data, "malloc", errno); 62 | } 63 | return ret; 64 | } 65 | 66 | /* Free memory. */ 67 | 68 | void 69 | backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED, 70 | void *p, size_t size ATTRIBUTE_UNUSED, 71 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, 72 | void *data ATTRIBUTE_UNUSED) 73 | { 74 | free (p); 75 | } 76 | 77 | /* Grow VEC by SIZE bytes. */ 78 | 79 | void * 80 | backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED, 81 | size_t size, backtrace_error_callback error_callback, 82 | void *data, struct backtrace_vector *vec) 83 | { 84 | void *ret; 85 | 86 | if (size > vec->alc) 87 | { 88 | size_t alc; 89 | void *base; 90 | 91 | if (vec->size == 0) 92 | alc = 32 * size; 93 | else if (vec->size >= 4096) 94 | alc = vec->size + 4096; 95 | else 96 | alc = 2 * vec->size; 97 | 98 | if (alc < vec->size + size) 99 | alc = vec->size + size; 100 | 101 | base = realloc (vec->base, alc); 102 | if (base == NULL) 103 | { 104 | error_callback (data, "realloc", errno); 105 | return NULL; 106 | } 107 | 108 | vec->base = base; 109 | vec->alc = alc - vec->size; 110 | } 111 | 112 | ret = (char *) vec->base + vec->size; 113 | vec->size += size; 114 | vec->alc -= size; 115 | return ret; 116 | } 117 | 118 | /* Finish the current allocation on VEC. */ 119 | 120 | void * 121 | backtrace_vector_finish (struct backtrace_state *state, 122 | struct backtrace_vector *vec, 123 | backtrace_error_callback error_callback, 124 | void *data) 125 | { 126 | void *ret; 127 | 128 | /* With this allocator we call realloc in backtrace_vector_grow, 129 | which means we can't easily reuse the memory here. So just 130 | release it. */ 131 | if (!backtrace_vector_release (state, vec, error_callback, data)) 132 | return NULL; 133 | ret = vec->base; 134 | vec->base = NULL; 135 | vec->size = 0; 136 | vec->alc = 0; 137 | return ret; 138 | } 139 | 140 | /* Release any extra space allocated for VEC. */ 141 | 142 | int 143 | backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED, 144 | struct backtrace_vector *vec, 145 | backtrace_error_callback error_callback, 146 | void *data) 147 | { 148 | vec->base = realloc (vec->base, vec->size); 149 | if (vec->base == NULL) 150 | { 151 | error_callback (data, "realloc", errno); 152 | return 0; 153 | } 154 | vec->alc = 0; 155 | return 1; 156 | } 157 | -------------------------------------------------------------------------------- /src/libbacktrace/atomic.c: -------------------------------------------------------------------------------- 1 | /* atomic.c -- Support for atomic functions if not present. 2 | Copyright (C) 2013-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #include "backtrace.h" 38 | #include "backtrace-supported.h" 39 | #include "internal.h" 40 | 41 | /* This file holds implementations of the atomic functions that are 42 | used if the host compiler has the sync functions but not the atomic 43 | functions, as is true of versions of GCC before 4.7. */ 44 | 45 | #if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS) 46 | 47 | /* Do an atomic load of a pointer. */ 48 | 49 | void * 50 | backtrace_atomic_load_pointer (void *arg) 51 | { 52 | void **pp; 53 | void *p; 54 | 55 | pp = (void **) arg; 56 | p = *pp; 57 | while (!__sync_bool_compare_and_swap (pp, p, p)) 58 | p = *pp; 59 | return p; 60 | } 61 | 62 | /* Do an atomic load of an int. */ 63 | 64 | int 65 | backtrace_atomic_load_int (int *p) 66 | { 67 | int i; 68 | 69 | i = *p; 70 | while (!__sync_bool_compare_and_swap (p, i, i)) 71 | i = *p; 72 | return i; 73 | } 74 | 75 | /* Do an atomic store of a pointer. */ 76 | 77 | void 78 | backtrace_atomic_store_pointer (void *arg, void *p) 79 | { 80 | void **pp; 81 | void *old; 82 | 83 | pp = (void **) arg; 84 | old = *pp; 85 | while (!__sync_bool_compare_and_swap (pp, old, p)) 86 | old = *pp; 87 | } 88 | 89 | /* Do an atomic store of a size_t value. */ 90 | 91 | void 92 | backtrace_atomic_store_size_t (size_t *p, size_t v) 93 | { 94 | size_t old; 95 | 96 | old = *p; 97 | while (!__sync_bool_compare_and_swap (p, old, v)) 98 | old = *p; 99 | } 100 | 101 | /* Do an atomic store of a int value. */ 102 | 103 | void 104 | backtrace_atomic_store_int (int *p, int v) 105 | { 106 | size_t old; 107 | 108 | old = *p; 109 | while (!__sync_bool_compare_and_swap (p, old, v)) 110 | old = *p; 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/libbacktrace/backtrace-supported.h: -------------------------------------------------------------------------------- 1 | /* backtrace-supported.h.in -- Whether stack backtrace is supported. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | /* The file backtrace-supported.h.in is used by configure to generate 34 | the file backtrace-supported.h. The file backtrace-supported.h may 35 | be #include'd to see whether the backtrace library will be able to 36 | get a backtrace and produce symbolic information. */ 37 | 38 | 39 | /* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library 40 | should work, 0 if it will not. Libraries may #include this to make 41 | other arrangements. */ 42 | 43 | #define BACKTRACE_SUPPORTED 1 44 | 45 | /* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace 46 | library will call malloc as it works, 0 if it will call mmap 47 | instead. This may be used to determine whether it is safe to call 48 | the backtrace functions from a signal handler. In general this 49 | only applies to calls like backtrace and backtrace_pcinfo. It does 50 | not apply to backtrace_simple, which never calls malloc. It does 51 | not apply to backtrace_print, which always calls fprintf and 52 | therefore malloc. */ 53 | 54 | #define BACKTRACE_USES_MALLOC 0 55 | 56 | /* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace 57 | library is configured with threading support, 0 if not. If this is 58 | 0, the threaded parameter to backtrace_create_state must be passed 59 | as 0. */ 60 | 61 | #define BACKTRACE_SUPPORTS_THREADS 1 62 | 63 | /* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo 64 | will work for variables. It will always work for functions. */ 65 | 66 | #define BACKTRACE_SUPPORTS_DATA 1 67 | -------------------------------------------------------------------------------- /src/libbacktrace/backtrace-supported.h.in: -------------------------------------------------------------------------------- 1 | /* backtrace-supported.h.in -- Whether stack backtrace is supported. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | /* The file backtrace-supported.h.in is used by configure to generate 34 | the file backtrace-supported.h. The file backtrace-supported.h may 35 | be #include'd to see whether the backtrace library will be able to 36 | get a backtrace and produce symbolic information. */ 37 | 38 | 39 | /* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library 40 | should work, 0 if it will not. Libraries may #include this to make 41 | other arrangements. */ 42 | 43 | #define BACKTRACE_SUPPORTED @BACKTRACE_SUPPORTED@ 44 | 45 | /* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace 46 | library will call malloc as it works, 0 if it will call mmap 47 | instead. This may be used to determine whether it is safe to call 48 | the backtrace functions from a signal handler. In general this 49 | only applies to calls like backtrace and backtrace_pcinfo. It does 50 | not apply to backtrace_simple, which never calls malloc. It does 51 | not apply to backtrace_print, which always calls fprintf and 52 | therefore malloc. */ 53 | 54 | #define BACKTRACE_USES_MALLOC @BACKTRACE_USES_MALLOC@ 55 | 56 | /* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace 57 | library is configured with threading support, 0 if not. If this is 58 | 0, the threaded parameter to backtrace_create_state must be passed 59 | as 0. */ 60 | 61 | #define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@ 62 | 63 | /* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo 64 | will work for variables. It will always work for functions. */ 65 | 66 | #define BACKTRACE_SUPPORTS_DATA @BACKTRACE_SUPPORTS_DATA@ 67 | -------------------------------------------------------------------------------- /src/libbacktrace/backtrace.c: -------------------------------------------------------------------------------- 1 | /* backtrace.c -- Entry point for stack backtrace library. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #include "unwind.h" 38 | #include "backtrace.h" 39 | #include "internal.h" 40 | 41 | /* The main backtrace_full routine. */ 42 | 43 | /* Data passed through _Unwind_Backtrace. */ 44 | 45 | struct backtrace_data 46 | { 47 | /* Number of frames to skip. */ 48 | int skip; 49 | /* Library state. */ 50 | struct backtrace_state *state; 51 | /* Callback routine. */ 52 | backtrace_full_callback callback; 53 | /* Error callback routine. */ 54 | backtrace_error_callback error_callback; 55 | /* Data to pass to callback routines. */ 56 | void *data; 57 | /* Value to return from backtrace_full. */ 58 | int ret; 59 | /* Whether there is any memory available. */ 60 | int can_alloc; 61 | }; 62 | 63 | /* Unwind library callback routine. This is passed to 64 | _Unwind_Backtrace. */ 65 | 66 | static _Unwind_Reason_Code 67 | unwind (struct _Unwind_Context *context, void *vdata) 68 | { 69 | struct backtrace_data *bdata = (struct backtrace_data *) vdata; 70 | uintptr_t pc; 71 | int ip_before_insn = 0; 72 | 73 | #ifdef HAVE_GETIPINFO 74 | pc = _Unwind_GetIPInfo (context, &ip_before_insn); 75 | #else 76 | pc = _Unwind_GetIP (context); 77 | #endif 78 | 79 | if (bdata->skip > 0) 80 | { 81 | --bdata->skip; 82 | return _URC_NO_REASON; 83 | } 84 | 85 | if (!ip_before_insn) 86 | --pc; 87 | 88 | if (!bdata->can_alloc) 89 | bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL); 90 | else 91 | bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback, 92 | bdata->error_callback, bdata->data); 93 | if (bdata->ret != 0) 94 | return _URC_END_OF_STACK; 95 | 96 | return _URC_NO_REASON; 97 | } 98 | 99 | /* Get a stack backtrace. */ 100 | 101 | int 102 | backtrace_full (struct backtrace_state *state, int skip, 103 | backtrace_full_callback callback, 104 | backtrace_error_callback error_callback, void *data) 105 | { 106 | struct backtrace_data bdata; 107 | void *p; 108 | 109 | bdata.skip = skip + 1; 110 | bdata.state = state; 111 | bdata.callback = callback; 112 | bdata.error_callback = error_callback; 113 | bdata.data = data; 114 | bdata.ret = 0; 115 | 116 | /* If we can't allocate any memory at all, don't try to produce 117 | file/line information. */ 118 | p = backtrace_alloc (state, 4096, NULL, NULL); 119 | if (p == NULL) 120 | bdata.can_alloc = 0; 121 | else 122 | { 123 | backtrace_free (state, p, 4096, NULL, NULL); 124 | bdata.can_alloc = 1; 125 | } 126 | 127 | _Unwind_Backtrace (unwind, &bdata); 128 | return bdata.ret; 129 | } 130 | -------------------------------------------------------------------------------- /src/libbacktrace/config.h: -------------------------------------------------------------------------------- 1 | /* config.h. Generated from config.h.in by configure. */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* ELF size: 32 or 64 */ 5 | #define BACKTRACE_ELF_SIZE 64 6 | 7 | /* Define to 1 if you have the __atomic functions */ 8 | #define HAVE_ATOMIC_FUNCTIONS 1 9 | 10 | /* Define to 1 if you have the declaration of `strnlen', and to 0 if you 11 | don't. */ 12 | #define HAVE_DECL_STRNLEN 1 13 | 14 | /* Define to 1 if you have the header file. */ 15 | #define HAVE_DLFCN_H 1 16 | 17 | /* Define if dl_iterate_phdr is available. */ 18 | #define HAVE_DL_ITERATE_PHDR 1 19 | 20 | /* Define to 1 if you have the fcntl function */ 21 | #define HAVE_FCNTL 1 22 | 23 | /* Define if getexecname is available. */ 24 | /* #undef HAVE_GETEXECNAME */ 25 | 26 | /* Define if _Unwind_GetIPInfo is available. */ 27 | #define HAVE_GETIPINFO 1 28 | 29 | /* Define to 1 if you have the header file. */ 30 | #define HAVE_INTTYPES_H 1 31 | 32 | /* Define to 1 if you have the header file. */ 33 | #define HAVE_LINK_H 1 34 | 35 | /* Define to 1 if you have the header file. */ 36 | #define HAVE_MEMORY_H 1 37 | 38 | /* Define to 1 if you have the header file. */ 39 | #define HAVE_STDINT_H 1 40 | 41 | /* Define to 1 if you have the header file. */ 42 | #define HAVE_STDLIB_H 1 43 | 44 | /* Define to 1 if you have the header file. */ 45 | #define HAVE_STRINGS_H 1 46 | 47 | /* Define to 1 if you have the header file. */ 48 | #define HAVE_STRING_H 1 49 | 50 | /* Define to 1 if you have the __sync functions */ 51 | #define HAVE_SYNC_FUNCTIONS 1 52 | 53 | /* Define to 1 if you have the header file. */ 54 | #define HAVE_SYS_MMAN_H 1 55 | 56 | /* Define to 1 if you have the header file. */ 57 | #define HAVE_SYS_STAT_H 1 58 | 59 | /* Define to 1 if you have the header file. */ 60 | #define HAVE_SYS_TYPES_H 1 61 | 62 | /* Define to 1 if you have the header file. */ 63 | #define HAVE_UNISTD_H 1 64 | 65 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 66 | */ 67 | #define LT_OBJDIR ".libs/" 68 | 69 | /* Define to the address where bug reports for this package should be sent. */ 70 | #define PACKAGE_BUGREPORT "" 71 | 72 | /* Define to the full name of this package. */ 73 | #define PACKAGE_NAME "package-unused" 74 | 75 | /* Define to the full name and version of this package. */ 76 | #define PACKAGE_STRING "package-unused version-unused" 77 | 78 | /* Define to the one symbol short name of this package. */ 79 | #define PACKAGE_TARNAME "libbacktrace" 80 | 81 | /* Define to the home page for this package. */ 82 | #define PACKAGE_URL "" 83 | 84 | /* Define to the version of this package. */ 85 | #define PACKAGE_VERSION "version-unused" 86 | 87 | /* The size of `char', as computed by sizeof. */ 88 | /* #undef SIZEOF_CHAR */ 89 | 90 | /* The size of `int', as computed by sizeof. */ 91 | /* #undef SIZEOF_INT */ 92 | 93 | /* The size of `long', as computed by sizeof. */ 94 | /* #undef SIZEOF_LONG */ 95 | 96 | /* The size of `short', as computed by sizeof. */ 97 | /* #undef SIZEOF_SHORT */ 98 | 99 | /* The size of `void *', as computed by sizeof. */ 100 | /* #undef SIZEOF_VOID_P */ 101 | 102 | /* Define to 1 if you have the ANSI C header files. */ 103 | #define STDC_HEADERS 1 104 | 105 | /* Enable extensions on AIX 3, Interix. */ 106 | #ifndef _ALL_SOURCE 107 | # define _ALL_SOURCE 1 108 | #endif 109 | /* Enable GNU extensions on systems that have them. */ 110 | #ifndef _GNU_SOURCE 111 | # define _GNU_SOURCE 1 112 | #endif 113 | /* Enable threading extensions on Solaris. */ 114 | #ifndef _POSIX_PTHREAD_SEMANTICS 115 | # define _POSIX_PTHREAD_SEMANTICS 1 116 | #endif 117 | /* Enable extensions on HP NonStop. */ 118 | #ifndef _TANDEM_SOURCE 119 | # define _TANDEM_SOURCE 1 120 | #endif 121 | /* Enable general extensions on Solaris. */ 122 | #ifndef __EXTENSIONS__ 123 | # define __EXTENSIONS__ 1 124 | #endif 125 | 126 | 127 | /* Define to 1 if on MINIX. */ 128 | /* #undef _MINIX */ 129 | 130 | /* Define to 2 if the system does not provide POSIX.1 features except with 131 | this defined. */ 132 | /* #undef _POSIX_1_SOURCE */ 133 | 134 | /* Define to 1 if you need to in order for `stat' and other things to work. */ 135 | /* #undef _POSIX_SOURCE */ 136 | -------------------------------------------------------------------------------- /src/libbacktrace/config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* ELF size: 32 or 64 */ 4 | #undef BACKTRACE_ELF_SIZE 5 | 6 | /* Define to 1 if you have the __atomic functions */ 7 | #undef HAVE_ATOMIC_FUNCTIONS 8 | 9 | /* Define to 1 if you have the declaration of `strnlen', and to 0 if you 10 | don't. */ 11 | #undef HAVE_DECL_STRNLEN 12 | 13 | /* Define to 1 if you have the header file. */ 14 | #undef HAVE_DLFCN_H 15 | 16 | /* Define if dl_iterate_phdr is available. */ 17 | #undef HAVE_DL_ITERATE_PHDR 18 | 19 | /* Define to 1 if you have the fcntl function */ 20 | #undef HAVE_FCNTL 21 | 22 | /* Define if getexecname is available. */ 23 | #undef HAVE_GETEXECNAME 24 | 25 | /* Define if _Unwind_GetIPInfo is available. */ 26 | #undef HAVE_GETIPINFO 27 | 28 | /* Define to 1 if you have the header file. */ 29 | #undef HAVE_INTTYPES_H 30 | 31 | /* Define to 1 if you have the header file. */ 32 | #undef HAVE_LINK_H 33 | 34 | /* Define to 1 if you have the header file. */ 35 | #undef HAVE_MEMORY_H 36 | 37 | /* Define to 1 if you have the header file. */ 38 | #undef HAVE_STDINT_H 39 | 40 | /* Define to 1 if you have the header file. */ 41 | #undef HAVE_STDLIB_H 42 | 43 | /* Define to 1 if you have the header file. */ 44 | #undef HAVE_STRINGS_H 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #undef HAVE_STRING_H 48 | 49 | /* Define to 1 if you have the __sync functions */ 50 | #undef HAVE_SYNC_FUNCTIONS 51 | 52 | /* Define to 1 if you have the header file. */ 53 | #undef HAVE_SYS_MMAN_H 54 | 55 | /* Define to 1 if you have the header file. */ 56 | #undef HAVE_SYS_STAT_H 57 | 58 | /* Define to 1 if you have the header file. */ 59 | #undef HAVE_SYS_TYPES_H 60 | 61 | /* Define to 1 if you have the header file. */ 62 | #undef HAVE_UNISTD_H 63 | 64 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 65 | */ 66 | #undef LT_OBJDIR 67 | 68 | /* Define to the address where bug reports for this package should be sent. */ 69 | #undef PACKAGE_BUGREPORT 70 | 71 | /* Define to the full name of this package. */ 72 | #undef PACKAGE_NAME 73 | 74 | /* Define to the full name and version of this package. */ 75 | #undef PACKAGE_STRING 76 | 77 | /* Define to the one symbol short name of this package. */ 78 | #undef PACKAGE_TARNAME 79 | 80 | /* Define to the home page for this package. */ 81 | #undef PACKAGE_URL 82 | 83 | /* Define to the version of this package. */ 84 | #undef PACKAGE_VERSION 85 | 86 | /* The size of `char', as computed by sizeof. */ 87 | #undef SIZEOF_CHAR 88 | 89 | /* The size of `int', as computed by sizeof. */ 90 | #undef SIZEOF_INT 91 | 92 | /* The size of `long', as computed by sizeof. */ 93 | #undef SIZEOF_LONG 94 | 95 | /* The size of `short', as computed by sizeof. */ 96 | #undef SIZEOF_SHORT 97 | 98 | /* The size of `void *', as computed by sizeof. */ 99 | #undef SIZEOF_VOID_P 100 | 101 | /* Define to 1 if you have the ANSI C header files. */ 102 | #undef STDC_HEADERS 103 | 104 | /* Enable extensions on AIX 3, Interix. */ 105 | #ifndef _ALL_SOURCE 106 | # undef _ALL_SOURCE 107 | #endif 108 | /* Enable GNU extensions on systems that have them. */ 109 | #ifndef _GNU_SOURCE 110 | # undef _GNU_SOURCE 111 | #endif 112 | /* Enable threading extensions on Solaris. */ 113 | #ifndef _POSIX_PTHREAD_SEMANTICS 114 | # undef _POSIX_PTHREAD_SEMANTICS 115 | #endif 116 | /* Enable extensions on HP NonStop. */ 117 | #ifndef _TANDEM_SOURCE 118 | # undef _TANDEM_SOURCE 119 | #endif 120 | /* Enable general extensions on Solaris. */ 121 | #ifndef __EXTENSIONS__ 122 | # undef __EXTENSIONS__ 123 | #endif 124 | 125 | 126 | /* Define to 1 if on MINIX. */ 127 | #undef _MINIX 128 | 129 | /* Define to 2 if the system does not provide POSIX.1 features except with 130 | this defined. */ 131 | #undef _POSIX_1_SOURCE 132 | 133 | /* Define to 1 if you need to in order for `stat' and other things to work. */ 134 | #undef _POSIX_SOURCE 135 | -------------------------------------------------------------------------------- /src/libbacktrace/fileline.c: -------------------------------------------------------------------------------- 1 | /* fileline.c -- Get file and line number information in a backtrace. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "backtrace.h" 42 | #include "internal.h" 43 | 44 | #ifndef HAVE_GETEXECNAME 45 | #define getexecname() NULL 46 | #endif 47 | 48 | /* Initialize the fileline information from the executable. Returns 1 49 | on success, 0 on failure. */ 50 | 51 | static int 52 | fileline_initialize (struct backtrace_state *state, 53 | backtrace_error_callback error_callback, void *data) 54 | { 55 | int failed; 56 | fileline fileline_fn; 57 | int pass; 58 | int called_error_callback; 59 | int descriptor; 60 | 61 | if (!state->threaded) 62 | failed = state->fileline_initialization_failed; 63 | else 64 | failed = backtrace_atomic_load_int (&state->fileline_initialization_failed); 65 | 66 | if (failed) 67 | { 68 | error_callback (data, "failed to read executable information", -1); 69 | return 0; 70 | } 71 | 72 | if (!state->threaded) 73 | fileline_fn = state->fileline_fn; 74 | else 75 | fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); 76 | if (fileline_fn != NULL) 77 | return 1; 78 | 79 | /* We have not initialized the information. Do it now. */ 80 | 81 | descriptor = -1; 82 | called_error_callback = 0; 83 | for (pass = 0; pass < 4; ++pass) 84 | { 85 | const char *filename; 86 | int does_not_exist; 87 | 88 | switch (pass) 89 | { 90 | case 0: 91 | filename = state->filename; 92 | break; 93 | case 1: 94 | filename = getexecname (); 95 | break; 96 | case 2: 97 | filename = "/proc/self/exe"; 98 | break; 99 | case 3: 100 | filename = "/proc/curproc/file"; 101 | break; 102 | default: 103 | abort (); 104 | } 105 | 106 | if (filename == NULL) 107 | continue; 108 | 109 | descriptor = backtrace_open (filename, error_callback, data, 110 | &does_not_exist); 111 | if (descriptor < 0 && !does_not_exist) 112 | { 113 | called_error_callback = 1; 114 | break; 115 | } 116 | if (descriptor >= 0) 117 | break; 118 | } 119 | 120 | if (descriptor < 0) 121 | { 122 | if (!called_error_callback) 123 | { 124 | if (state->filename != NULL) 125 | error_callback (data, state->filename, ENOENT); 126 | else 127 | error_callback (data, 128 | "libbacktrace could not find executable to open", 129 | 0); 130 | } 131 | failed = 1; 132 | } 133 | 134 | if (!failed) 135 | { 136 | if (!backtrace_initialize (state, descriptor, error_callback, data, 137 | &fileline_fn)) 138 | failed = 1; 139 | } 140 | 141 | if (failed) 142 | { 143 | if (!state->threaded) 144 | state->fileline_initialization_failed = 1; 145 | else 146 | backtrace_atomic_store_int (&state->fileline_initialization_failed, 1); 147 | return 0; 148 | } 149 | 150 | if (!state->threaded) 151 | state->fileline_fn = fileline_fn; 152 | else 153 | { 154 | backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn); 155 | 156 | /* Note that if two threads initialize at once, one of the data 157 | sets may be leaked. */ 158 | } 159 | 160 | return 1; 161 | } 162 | 163 | /* Given a PC, find the file name, line number, and function name. */ 164 | 165 | int 166 | backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, 167 | backtrace_full_callback callback, 168 | backtrace_error_callback error_callback, void *data) 169 | { 170 | if (!fileline_initialize (state, error_callback, data)) 171 | return 0; 172 | 173 | if (state->fileline_initialization_failed) 174 | return 0; 175 | 176 | return state->fileline_fn (state, pc, callback, error_callback, data); 177 | } 178 | 179 | /* Given a PC, find the symbol for it, and its value. */ 180 | 181 | int 182 | backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, 183 | backtrace_syminfo_callback callback, 184 | backtrace_error_callback error_callback, void *data) 185 | { 186 | if (!fileline_initialize (state, error_callback, data)) 187 | return 0; 188 | 189 | if (state->fileline_initialization_failed) 190 | return 0; 191 | 192 | state->syminfo_fn (state, pc, callback, error_callback, data); 193 | return 1; 194 | } 195 | -------------------------------------------------------------------------------- /src/libbacktrace/filenames.h: -------------------------------------------------------------------------------- 1 | /* Macros for taking apart, interpreting and processing file names. 2 | 3 | These are here because some non-Posix (a.k.a. DOSish) systems have 4 | drive letter brain-damage at the beginning of an absolute file name, 5 | use forward- and back-slash in path names interchangeably, and 6 | some of them have case-insensitive file names. 7 | 8 | Copyright (C) 2000-2015 Free Software Foundation, Inc. 9 | 10 | This file is part of BFD, the Binary File Descriptor library. 11 | 12 | This program is free software; you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation; either version 2 of the License, or 15 | (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License 23 | along with this program; if not, write to the Free Software 24 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 25 | 26 | #ifndef FILENAMES_H 27 | #define FILENAMES_H 28 | 29 | #include "hashtab.h" /* for hashval_t */ 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) 36 | # ifndef HAVE_DOS_BASED_FILE_SYSTEM 37 | # define HAVE_DOS_BASED_FILE_SYSTEM 1 38 | # endif 39 | # ifndef HAVE_CASE_INSENSITIVE_FILE_SYSTEM 40 | # define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 41 | # endif 42 | # define HAS_DRIVE_SPEC(f) HAS_DOS_DRIVE_SPEC (f) 43 | # define IS_DIR_SEPARATOR(c) IS_DOS_DIR_SEPARATOR (c) 44 | # define IS_ABSOLUTE_PATH(f) IS_DOS_ABSOLUTE_PATH (f) 45 | #else /* not DOSish */ 46 | # if defined(__APPLE__) 47 | # ifndef HAVE_CASE_INSENSITIVE_FILE_SYSTEM 48 | # define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 49 | # endif 50 | # endif /* __APPLE__ */ 51 | # define HAS_DRIVE_SPEC(f) (0) 52 | # define IS_DIR_SEPARATOR(c) IS_UNIX_DIR_SEPARATOR (c) 53 | # define IS_ABSOLUTE_PATH(f) IS_UNIX_ABSOLUTE_PATH (f) 54 | #endif 55 | 56 | #define IS_DIR_SEPARATOR_1(dos_based, c) \ 57 | (((c) == '/') \ 58 | || (((c) == '\\') && (dos_based))) 59 | 60 | #define HAS_DRIVE_SPEC_1(dos_based, f) \ 61 | ((f)[0] && ((f)[1] == ':') && (dos_based)) 62 | 63 | /* Remove the drive spec from F, assuming HAS_DRIVE_SPEC (f). 64 | The result is a pointer to the remainder of F. */ 65 | #define STRIP_DRIVE_SPEC(f) ((f) + 2) 66 | 67 | #define IS_DOS_DIR_SEPARATOR(c) IS_DIR_SEPARATOR_1 (1, c) 68 | #define IS_DOS_ABSOLUTE_PATH(f) IS_ABSOLUTE_PATH_1 (1, f) 69 | #define HAS_DOS_DRIVE_SPEC(f) HAS_DRIVE_SPEC_1 (1, f) 70 | 71 | #define IS_UNIX_DIR_SEPARATOR(c) IS_DIR_SEPARATOR_1 (0, c) 72 | #define IS_UNIX_ABSOLUTE_PATH(f) IS_ABSOLUTE_PATH_1 (0, f) 73 | 74 | /* Note that when DOS_BASED is true, IS_ABSOLUTE_PATH accepts d:foo as 75 | well, although it is only semi-absolute. This is because the users 76 | of IS_ABSOLUTE_PATH want to know whether to prepend the current 77 | working directory to a file name, which should not be done with a 78 | name like d:foo. */ 79 | #define IS_ABSOLUTE_PATH_1(dos_based, f) \ 80 | (IS_DIR_SEPARATOR_1 (dos_based, (f)[0]) \ 81 | || HAS_DRIVE_SPEC_1 (dos_based, f)) 82 | 83 | extern int filename_cmp (const char *s1, const char *s2); 84 | #define FILENAME_CMP(s1, s2) filename_cmp(s1, s2) 85 | 86 | extern int filename_ncmp (const char *s1, const char *s2, 87 | size_t n); 88 | 89 | extern hashval_t filename_hash (const void *s); 90 | 91 | extern int filename_eq (const void *s1, const void *s2); 92 | 93 | extern int canonical_filename_eq (const char *a, const char *b); 94 | 95 | #ifdef __cplusplus 96 | } 97 | #endif 98 | 99 | #endif /* FILENAMES_H */ 100 | -------------------------------------------------------------------------------- /src/libbacktrace/filetype.awk: -------------------------------------------------------------------------------- 1 | # An awk script to determine the type of a file. 2 | /\177ELF\001/ { if (NR == 1) { print "elf32"; exit } } 3 | /\177ELF\002/ { if (NR == 1) { print "elf64"; exit } } 4 | /\114\001/ { if (NR == 1) { print "pecoff"; exit } } 5 | /\144\206/ { if (NR == 1) { print "pecoff"; exit } } 6 | -------------------------------------------------------------------------------- /src/libbacktrace/mmapio.c: -------------------------------------------------------------------------------- 1 | /* mmapio.c -- File views using mmap. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "backtrace.h" 41 | #include "internal.h" 42 | 43 | #ifndef MAP_FAILED 44 | #define MAP_FAILED ((void *)-1) 45 | #endif 46 | 47 | /* This file implements file views and memory allocation when mmap is 48 | available. */ 49 | 50 | /* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */ 51 | 52 | int 53 | backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, 54 | int descriptor, off_t offset, size_t size, 55 | backtrace_error_callback error_callback, 56 | void *data, struct backtrace_view *view) 57 | { 58 | size_t pagesize; 59 | unsigned int inpage; 60 | off_t pageoff; 61 | void *map; 62 | 63 | pagesize = getpagesize (); 64 | inpage = offset % pagesize; 65 | pageoff = offset - inpage; 66 | 67 | size += inpage; 68 | size = (size + (pagesize - 1)) & ~ (pagesize - 1); 69 | 70 | map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); 71 | if (map == MAP_FAILED) 72 | { 73 | error_callback (data, "mmap", errno); 74 | return 0; 75 | } 76 | 77 | view->data = (char *) map + inpage; 78 | view->base = map; 79 | view->len = size; 80 | 81 | return 1; 82 | } 83 | 84 | /* Release a view read by backtrace_get_view. */ 85 | 86 | void 87 | backtrace_release_view (struct backtrace_state *state ATTRIBUTE_UNUSED, 88 | struct backtrace_view *view, 89 | backtrace_error_callback error_callback, 90 | void *data) 91 | { 92 | union { 93 | const void *cv; 94 | void *v; 95 | } const_cast; 96 | 97 | const_cast.cv = view->base; 98 | if (munmap (const_cast.v, view->len) < 0) 99 | error_callback (data, "munmap", errno); 100 | } 101 | -------------------------------------------------------------------------------- /src/libbacktrace/nounwind.c: -------------------------------------------------------------------------------- 1 | /* backtrace.c -- Entry point for stack backtrace library. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #include "backtrace.h" 38 | 39 | #include "internal.h" 40 | 41 | /* This source file is compiled if the unwind library is not 42 | available. */ 43 | 44 | int 45 | backtrace_full (struct backtrace_state *state ATTRIBUTE_UNUSED, 46 | int skip ATTRIBUTE_UNUSED, 47 | backtrace_full_callback callback ATTRIBUTE_UNUSED, 48 | backtrace_error_callback error_callback, void *data) 49 | { 50 | error_callback (data, 51 | "no stack trace because unwind library not available", 52 | 0); 53 | return 0; 54 | } 55 | 56 | int 57 | backtrace_simple (struct backtrace_state *state ATTRIBUTE_UNUSED, 58 | int skip ATTRIBUTE_UNUSED, 59 | backtrace_simple_callback callback ATTRIBUTE_UNUSED, 60 | backtrace_error_callback error_callback, void *data) 61 | { 62 | error_callback (data, 63 | "no stack trace because unwind library not available", 64 | 0); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /src/libbacktrace/posix.c: -------------------------------------------------------------------------------- 1 | /* posix.c -- POSIX file I/O routines for the backtrace library. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "backtrace.h" 42 | #include "internal.h" 43 | 44 | #ifndef O_BINARY 45 | #define O_BINARY 0 46 | #endif 47 | 48 | #ifndef O_CLOEXEC 49 | #define O_CLOEXEC 0 50 | #endif 51 | 52 | #ifndef FD_CLOEXEC 53 | #define FD_CLOEXEC 1 54 | #endif 55 | 56 | /* Open a file for reading. */ 57 | 58 | int 59 | backtrace_open (const char *filename, backtrace_error_callback error_callback, 60 | void *data, int *does_not_exist) 61 | { 62 | int descriptor; 63 | 64 | if (does_not_exist != NULL) 65 | *does_not_exist = 0; 66 | 67 | descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC)); 68 | if (descriptor < 0) 69 | { 70 | if (does_not_exist != NULL && errno == ENOENT) 71 | *does_not_exist = 1; 72 | else 73 | error_callback (data, filename, errno); 74 | return -1; 75 | } 76 | 77 | #ifdef HAVE_FCNTL 78 | /* Set FD_CLOEXEC just in case the kernel does not support 79 | O_CLOEXEC. It doesn't matter if this fails for some reason. 80 | FIXME: At some point it should be safe to only do this if 81 | O_CLOEXEC == 0. */ 82 | fcntl (descriptor, F_SETFD, FD_CLOEXEC); 83 | #endif 84 | 85 | return descriptor; 86 | } 87 | 88 | /* Close DESCRIPTOR. */ 89 | 90 | int 91 | backtrace_close (int descriptor, backtrace_error_callback error_callback, 92 | void *data) 93 | { 94 | if (close (descriptor) < 0) 95 | { 96 | error_callback (data, "close", errno); 97 | return 0; 98 | } 99 | return 1; 100 | } 101 | -------------------------------------------------------------------------------- /src/libbacktrace/print.c: -------------------------------------------------------------------------------- 1 | /* print.c -- Print the current backtrace. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "backtrace.h" 40 | #include "internal.h" 41 | 42 | /* Passed to callbacks. */ 43 | 44 | struct print_data 45 | { 46 | struct backtrace_state *state; 47 | FILE *f; 48 | }; 49 | 50 | /* Print one level of a backtrace. */ 51 | 52 | static int 53 | print_callback (void *data, uintptr_t pc, const char *filename, int lineno, 54 | const char *function) 55 | { 56 | struct print_data *pdata = (struct print_data *) data; 57 | 58 | fprintf (pdata->f, "0x%lx %s\n\t%s:%d\n", 59 | (unsigned long) pc, 60 | function == NULL ? "???" : function, 61 | filename == NULL ? "???" : filename, 62 | lineno); 63 | return 0; 64 | } 65 | 66 | /* Print errors to stderr. */ 67 | 68 | static void 69 | error_callback (void *data, const char *msg, int errnum) 70 | { 71 | struct print_data *pdata = (struct print_data *) data; 72 | 73 | if (pdata->state->filename != NULL) 74 | fprintf (stderr, "%s: ", pdata->state->filename); 75 | fprintf (stderr, "libbacktrace: %s", msg); 76 | if (errnum > 0) 77 | fprintf (stderr, ": %s", strerror (errnum)); 78 | fputc ('\n', stderr); 79 | } 80 | 81 | /* Print a backtrace. */ 82 | 83 | void 84 | backtrace_print (struct backtrace_state *state, int skip, FILE *f) 85 | { 86 | struct print_data data; 87 | 88 | data.state = state; 89 | data.f = f; 90 | backtrace_full (state, skip + 1, print_callback, error_callback, 91 | (void *) &data); 92 | } 93 | -------------------------------------------------------------------------------- /src/libbacktrace/read.c: -------------------------------------------------------------------------------- 1 | /* read.c -- File views without mmap. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "backtrace.h" 41 | #include "internal.h" 42 | 43 | /* This file implements file views when mmap is not available. */ 44 | 45 | /* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */ 46 | 47 | int 48 | backtrace_get_view (struct backtrace_state *state, int descriptor, 49 | off_t offset, size_t size, 50 | backtrace_error_callback error_callback, 51 | void *data, struct backtrace_view *view) 52 | { 53 | ssize_t got; 54 | 55 | if (lseek (descriptor, offset, SEEK_SET) < 0) 56 | { 57 | error_callback (data, "lseek", errno); 58 | return 0; 59 | } 60 | 61 | view->base = backtrace_alloc (state, size, error_callback, data); 62 | if (view->base == NULL) 63 | return 0; 64 | view->data = view->base; 65 | view->len = size; 66 | 67 | got = read (descriptor, view->base, size); 68 | if (got < 0) 69 | { 70 | error_callback (data, "read", errno); 71 | free (view->base); 72 | return 0; 73 | } 74 | 75 | if ((size_t) got < size) 76 | { 77 | error_callback (data, "file too short", 0); 78 | free (view->base); 79 | return 0; 80 | } 81 | 82 | return 1; 83 | } 84 | 85 | /* Release a view read by backtrace_get_view. */ 86 | 87 | void 88 | backtrace_release_view (struct backtrace_state *state, 89 | struct backtrace_view *view, 90 | backtrace_error_callback error_callback, 91 | void *data) 92 | { 93 | backtrace_free (state, view->base, view->len, error_callback, data); 94 | view->data = NULL; 95 | view->base = NULL; 96 | } 97 | -------------------------------------------------------------------------------- /src/libbacktrace/simple.c: -------------------------------------------------------------------------------- 1 | /* simple.c -- The backtrace_simple function. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include "unwind.h" 36 | #include "backtrace.h" 37 | 38 | /* The simple_backtrace routine. */ 39 | 40 | /* Data passed through _Unwind_Backtrace. */ 41 | 42 | struct backtrace_simple_data 43 | { 44 | /* Number of frames to skip. */ 45 | int skip; 46 | /* Library state. */ 47 | struct backtrace_state *state; 48 | /* Callback routine. */ 49 | backtrace_simple_callback callback; 50 | /* Error callback routine. */ 51 | backtrace_error_callback error_callback; 52 | /* Data to pass to callback routine. */ 53 | void *data; 54 | /* Value to return from backtrace. */ 55 | int ret; 56 | }; 57 | 58 | /* Unwind library callback routine. This is passd to 59 | _Unwind_Backtrace. */ 60 | 61 | static _Unwind_Reason_Code 62 | simple_unwind (struct _Unwind_Context *context, void *vdata) 63 | { 64 | struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata; 65 | uintptr_t pc; 66 | int ip_before_insn = 0; 67 | 68 | #ifdef HAVE_GETIPINFO 69 | pc = _Unwind_GetIPInfo (context, &ip_before_insn); 70 | #else 71 | pc = _Unwind_GetIP (context); 72 | #endif 73 | 74 | if (bdata->skip > 0) 75 | { 76 | --bdata->skip; 77 | return _URC_NO_REASON; 78 | } 79 | 80 | if (!ip_before_insn) 81 | --pc; 82 | 83 | bdata->ret = bdata->callback (bdata->data, pc); 84 | 85 | if (bdata->ret != 0) 86 | return _URC_END_OF_STACK; 87 | 88 | return _URC_NO_REASON; 89 | } 90 | 91 | /* Get a simple stack backtrace. */ 92 | 93 | int 94 | backtrace_simple (struct backtrace_state *state, int skip, 95 | backtrace_simple_callback callback, 96 | backtrace_error_callback error_callback, void *data) 97 | { 98 | struct backtrace_simple_data bdata; 99 | 100 | bdata.skip = skip + 1; 101 | bdata.state = state; 102 | bdata.callback = callback; 103 | bdata.error_callback = error_callback; 104 | bdata.data = data; 105 | bdata.ret = 0; 106 | _Unwind_Backtrace (simple_unwind, &bdata); 107 | return bdata.ret; 108 | } 109 | -------------------------------------------------------------------------------- /src/libbacktrace/sort.c: -------------------------------------------------------------------------------- 1 | /* sort.c -- Sort without allocating memory 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | 38 | #include "backtrace.h" 39 | #include "internal.h" 40 | 41 | /* The GNU glibc version of qsort allocates memory, which we must not 42 | do if we are invoked by a signal handler. So provide our own 43 | sort. */ 44 | 45 | static void 46 | swap (char *a, char *b, size_t size) 47 | { 48 | size_t i; 49 | 50 | for (i = 0; i < size; i++, a++, b++) 51 | { 52 | char t; 53 | 54 | t = *a; 55 | *a = *b; 56 | *b = t; 57 | } 58 | } 59 | 60 | void 61 | backtrace_qsort (void *basearg, size_t count, size_t size, 62 | int (*compar) (const void *, const void *)) 63 | { 64 | char *base = (char *) basearg; 65 | size_t i; 66 | size_t mid; 67 | 68 | tail_recurse: 69 | if (count < 2) 70 | return; 71 | 72 | /* The symbol table and DWARF tables, which is all we use this 73 | routine for, tend to be roughly sorted. Pick the middle element 74 | in the array as our pivot point, so that we are more likely to 75 | cut the array in half for each recursion step. */ 76 | swap (base, base + (count / 2) * size, size); 77 | 78 | mid = 0; 79 | for (i = 1; i < count; i++) 80 | { 81 | if ((*compar) (base, base + i * size) > 0) 82 | { 83 | ++mid; 84 | if (i != mid) 85 | swap (base + mid * size, base + i * size, size); 86 | } 87 | } 88 | 89 | if (mid > 0) 90 | swap (base, base + mid * size, size); 91 | 92 | /* Recurse with the smaller array, loop with the larger one. That 93 | ensures that our maximum stack depth is log count. */ 94 | if (2 * mid < count) 95 | { 96 | backtrace_qsort (base, mid, size, compar); 97 | base += (mid + 1) * size; 98 | count -= mid + 1; 99 | goto tail_recurse; 100 | } 101 | else 102 | { 103 | backtrace_qsort (base + (mid + 1) * size, count - (mid + 1), 104 | size, compar); 105 | count = mid; 106 | goto tail_recurse; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/libbacktrace/stamp-h1: -------------------------------------------------------------------------------- 1 | timestamp for config.h 2 | -------------------------------------------------------------------------------- /src/libbacktrace/state.c: -------------------------------------------------------------------------------- 1 | /* state.c -- Create the backtrace state. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | 38 | #include "backtrace.h" 39 | #include "backtrace-supported.h" 40 | #include "internal.h" 41 | 42 | /* Create the backtrace state. This will then be passed to all the 43 | other routines. */ 44 | 45 | struct backtrace_state * 46 | backtrace_create_state (const char *filename, int threaded, 47 | backtrace_error_callback error_callback, 48 | void *data) 49 | { 50 | struct backtrace_state init_state; 51 | struct backtrace_state *state; 52 | 53 | #ifndef HAVE_SYNC_FUNCTIONS 54 | if (threaded) 55 | { 56 | error_callback (data, "backtrace library does not support threads", 0); 57 | return NULL; 58 | } 59 | #endif 60 | 61 | memset (&init_state, 0, sizeof init_state); 62 | init_state.filename = filename; 63 | init_state.threaded = threaded; 64 | 65 | state = ((struct backtrace_state *) 66 | backtrace_alloc (&init_state, sizeof *state, error_callback, data)); 67 | if (state == NULL) 68 | return NULL; 69 | *state = init_state; 70 | 71 | return state; 72 | } 73 | -------------------------------------------------------------------------------- /src/libbacktrace/stest.c: -------------------------------------------------------------------------------- 1 | /* stest.c -- Test for libbacktrace internal sort function 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "backtrace.h" 41 | #include "internal.h" 42 | 43 | /* Test the local qsort implementation. */ 44 | 45 | #define MAX 10 46 | 47 | struct test 48 | { 49 | size_t count; 50 | int input[MAX]; 51 | int output[MAX]; 52 | }; 53 | 54 | static struct test tests[] = 55 | { 56 | { 57 | 10, 58 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 59 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } 60 | }, 61 | { 62 | 9, 63 | { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 64 | { 1, 2, 3, 4, 5, 6, 7, 8, 9 } 65 | }, 66 | { 67 | 10, 68 | { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 69 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 70 | }, 71 | { 72 | 9, 73 | { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 74 | { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 75 | }, 76 | { 77 | 10, 78 | { 2, 4, 6, 8, 10, 1, 3, 5, 7, 9 }, 79 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 80 | }, 81 | { 82 | 5, 83 | { 4, 5, 3, 1, 2 }, 84 | { 1, 2, 3, 4, 5 }, 85 | }, 86 | { 87 | 5, 88 | { 1, 1, 1, 1, 1 }, 89 | { 1, 1, 1, 1, 1 }, 90 | }, 91 | { 92 | 5, 93 | { 1, 1, 2, 1, 1 }, 94 | { 1, 1, 1, 1, 2 }, 95 | }, 96 | { 97 | 5, 98 | { 2, 1, 1, 1, 1 }, 99 | { 1, 1, 1, 1, 2 }, 100 | }, 101 | }; 102 | 103 | static int 104 | compare (const void *a, const void *b) 105 | { 106 | const int *ai = (const int *) a; 107 | const int *bi = (const int *) b; 108 | 109 | return *ai - *bi; 110 | } 111 | 112 | int 113 | main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) 114 | { 115 | int failures; 116 | size_t i; 117 | int a[MAX]; 118 | 119 | failures = 0; 120 | for (i = 0; i < sizeof tests / sizeof tests[0]; i++) 121 | { 122 | memcpy (a, tests[i].input, tests[i].count * sizeof (int)); 123 | backtrace_qsort (a, tests[i].count, sizeof (int), compare); 124 | if (memcmp (a, tests[i].output, tests[i].count * sizeof (int)) != 0) 125 | { 126 | size_t j; 127 | 128 | fprintf (stderr, "test %d failed:", (int) i); 129 | for (j = 0; j < tests[i].count; j++) 130 | fprintf (stderr, " %d", a[j]); 131 | fprintf (stderr, "\n"); 132 | ++failures; 133 | } 134 | } 135 | 136 | exit (failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS); 137 | } 138 | -------------------------------------------------------------------------------- /src/libbacktrace/unknown.c: -------------------------------------------------------------------------------- 1 | /* unknown.c -- used when backtrace configury does not know file format. 2 | Copyright (C) 2012-2017 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #include "backtrace.h" 38 | #include "internal.h" 39 | 40 | /* A trivial routine that always fails to find fileline data. */ 41 | 42 | static int 43 | unknown_fileline (struct backtrace_state *state ATTRIBUTE_UNUSED, 44 | uintptr_t pc, backtrace_full_callback callback, 45 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, 46 | void *data) 47 | 48 | { 49 | return callback (data, pc, NULL, 0, NULL); 50 | } 51 | 52 | /* Initialize the backtrace data when we don't know how to read the 53 | debug info. */ 54 | 55 | int 56 | backtrace_initialize (struct backtrace_state *state ATTRIBUTE_UNUSED, 57 | int descriptor ATTRIBUTE_UNUSED, 58 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, 59 | void *data ATTRIBUTE_UNUSED, fileline *fileline_fn) 60 | { 61 | state->fileline_data = NULL; 62 | *fileline_fn = unknown_fileline; 63 | return 1; 64 | } 65 | -------------------------------------------------------------------------------- /src/machine.c: -------------------------------------------------------------------------------- 1 | #include "machine.h" 2 | 3 | #include "vmprof.h" 4 | #include 5 | 6 | #ifdef VMPROF_UNIX 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | int vmp_machine_bits(void) 13 | { 14 | return sizeof(void*)*8; 15 | } 16 | 17 | const char * vmp_machine_os_name(void) 18 | { 19 | #ifdef _WIN32 20 | #ifdef _WIN64 21 | return "win64"; 22 | #endif 23 | return "win32"; 24 | #elif __APPLE__ 25 | #include "TargetConditionals.h" 26 | #if TARGET_OS_MAC 27 | return "mac os x"; 28 | #endif 29 | #elif __linux__ 30 | return "linux"; 31 | #elif __FreeBSD__ 32 | return "freebsd"; 33 | #else 34 | #error "Unknown compiler" 35 | #endif 36 | } 37 | 38 | long vmp_fd_to_path(int fd, char * buffer, long buffer_len) 39 | { 40 | #ifdef VMPROF_LINUX 41 | char proffs[24]; 42 | (void)snprintf(proffs, 24, "/proc/self/fd/%d", fd); 43 | return readlink(proffs, buffer, buffer_len); 44 | #elif defined(VMPROF_UNIX) && !defined(__FreeBSD__) 45 | fcntl(fd, F_GETPATH, buffer); 46 | return strlen(buffer); 47 | #endif 48 | return -1; 49 | } 50 | -------------------------------------------------------------------------------- /src/machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * What is the usual word size of the processor? 64bit? 32bit? 5 | */ 6 | int vmp_machine_bits(void); 7 | 8 | /** 9 | * Return the human readable name of the operating system. 10 | */ 11 | const char * vmp_machine_os_name(void); 12 | 13 | /** 14 | * Writes the filename into buffer. Returns -1 if the platform is not 15 | * implemented. 16 | */ 17 | long vmp_fd_to_path(int fd, char * buffer, long buffer_len); 18 | 19 | -------------------------------------------------------------------------------- /src/msiinttypes/changelog.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/src/msiinttypes/changelog.txt -------------------------------------------------------------------------------- /src/populate_frames.c: -------------------------------------------------------------------------------- 1 | /* This code was taken from https://github.com/GoogleCloudPlatform/cloud-profiler-python/blob/main/googlecloudprofiler/src/populate_frames.cc */ 2 | 3 | #include "populate_frames.h" 4 | 5 | #include 6 | 7 | 8 | // 0x030B0000 is 3.11. 9 | #define PY_311 0x030B0000 10 | #if PY_VERSION_HEX >= PY_311 11 | 12 | /** 13 | * The PyFrameObject structure members have been removed from the public C API 14 | * in 3.11: 15 | https://docs.python.org/3/whatsnew/3.11.html#pyframeobject-3-11-hiding. 16 | * 17 | * Instead, getters are provided which participate in reference counting; since 18 | * this code runs as part of the SIGPROF handler, it cannot modify Python 19 | * objects (including their refcounts) and the getters can't be used. Instead, 20 | * we expose the internal _PyInterpreterFrame and use that directly. 21 | * 22 | */ 23 | 24 | #define Py_BUILD_CORE 25 | #include "internal/pycore_frame.h" 26 | #undef Py_BUILD_CORE 27 | 28 | // Modified from 29 | // https://github.com/python/cpython/blob/v3.11.4/Python/pystate.c#L1278-L1285 30 | _PyInterpreterFrame *unsafe_PyThreadState_GetInterpreterFrame( 31 | PyThreadState *tstate) { 32 | assert(tstate != NULL); 33 | _PyInterpreterFrame *f = tstate->cframe->current_frame; 34 | while (f && _PyFrame_IsIncomplete(f)) { 35 | f = f->previous; 36 | } 37 | if (f == NULL) { 38 | return NULL; 39 | } 40 | return f; 41 | } 42 | 43 | // Modified from 44 | // https://github.com/python/cpython/blob/v3.11.4/Objects/frameobject.c#L1310-L1315 45 | // with refcounting removed 46 | PyCodeObject *unsafe_PyInterpreterFrame_GetCode( 47 | _PyInterpreterFrame *frame) { 48 | assert(frame != NULL); 49 | assert(!_PyFrame_IsIncomplete(frame)); 50 | PyCodeObject *code = frame->f_code; 51 | assert(code != NULL); 52 | return code; 53 | } 54 | 55 | // Modified from 56 | // https://github.com/python/cpython/blob/v3.11.4/Objects/frameobject.c#L1326-L1329 57 | // with refcounting removed 58 | _PyInterpreterFrame *unsafe_PyInterpreterFrame_GetBack( 59 | _PyInterpreterFrame *frame) { 60 | assert(frame != NULL); 61 | assert(!_PyFrame_IsIncomplete(frame)); 62 | _PyInterpreterFrame *prev = frame->previous; 63 | while (prev && _PyFrame_IsIncomplete(prev)) { 64 | prev = prev->previous; 65 | } 66 | return prev; 67 | } 68 | 69 | // Copied from 70 | // https://github.com/python/cpython/blob/v3.11.4/Python/frame.c#L165-L170 as 71 | // this function is not available in libpython 72 | int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame) { 73 | int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); 74 | return PyCode_Addr2Line(frame->f_code, addr); 75 | } 76 | #endif // PY_VERSION_HEX >= PY_311 -------------------------------------------------------------------------------- /src/populate_frames.h: -------------------------------------------------------------------------------- 1 | /* This code was taken from https://github.com/GoogleCloudPlatform/cloud-profiler-python/blob/main/googlecloudprofiler/src/populate_frames.h */ 2 | 3 | #ifndef pp_frames 4 | #define pp_frames 5 | 6 | #include 7 | 8 | #include 9 | 10 | #define Py_BUILD_CORE 11 | #include "internal/pycore_frame.h" 12 | #undef Py_BUILD_CORE 13 | 14 | _PyInterpreterFrame *unsafe_PyThreadState_GetInterpreterFrame(PyThreadState *tstate); 15 | 16 | PyCodeObject *unsafe_PyInterpreterFrame_GetCode(_PyInterpreterFrame *frame); 17 | 18 | _PyInterpreterFrame *unsafe_PyInterpreterFrame_GetBack(_PyInterpreterFrame *frame); 19 | 20 | int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/symboltable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _GNU_SOURCE 1 4 | 5 | int vmp_resolve_addr(void * addr, char * name, int name_len, int * lineno, 6 | char * srcfile, int srcfile_len); 7 | -------------------------------------------------------------------------------- /src/trampoline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Returns 0 on success 5 | */ 6 | int vmp_patch_callee_trampoline(void * callee_addr, void * vmprof_eval, void ** vmprof_eval_target); 7 | 8 | /** 9 | * Return 0 on success, -1 if the trampoline is not in place. 10 | * Any other value indicates a fatal error! 11 | */ 12 | int vmp_unpatch_callee_trampoline(void * callee_addr); 13 | -------------------------------------------------------------------------------- /src/unwind/vmprof_unwind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _XOPEN_SOURCE 700 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // copied from libunwind.h 10 | #ifdef X86_32 11 | #define UNW_REG_IP 8 12 | typedef uint32_t unw_word_t; 13 | typedef int32_t unw_sword_t; 14 | #elif defined(X86_64) 15 | #define UNW_REG_IP 16 16 | typedef uint64_t unw_word_t; 17 | typedef int64_t unw_sword_t; 18 | #elif defined (__powerpc64__) 19 | #define UNW_REG_IP 32 20 | typedef uint64_t unw_word_t; 21 | typedef int64_t unw_sword_t; 22 | #else 23 | // not supported platform 24 | #endif 25 | 26 | 27 | #define UNW_TDEP_CURSOR_LEN 127 28 | 29 | #ifdef VMP_SUPPORTS_NATIVE_PROFILING 30 | typedef struct unw_cursor 31 | { 32 | unw_word_t opaque[UNW_TDEP_CURSOR_LEN]; 33 | } 34 | unw_cursor_t; 35 | 36 | typedef struct unw_proc_info 37 | { 38 | unw_word_t start_ip; /* first IP covered by this procedure */ 39 | unw_word_t end_ip; /* first IP NOT covered by this procedure */ 40 | unw_word_t lsda; /* address of lang.-spec. data area (if any) */ 41 | unw_word_t handler; /* optional personality routine */ 42 | unw_word_t gp; /* global-pointer value for this procedure */ 43 | unw_word_t flags; /* misc. flags */ 44 | 45 | int format; /* unwind-info format (arch-specific) */ 46 | int unwind_info_size; /* size of the information (if applicable) */ 47 | void *unwind_info; /* unwind-info (arch-specific) */ 48 | } unw_proc_info_t; 49 | 50 | // end of copy 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/vmp_stack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmprof.h" 4 | 5 | #ifndef RPYTHON_VMPROF 6 | #if PY_VERSION_HEX >= 0x030b00f0 /* >= 3.11 */ 7 | #include "internal/pycore_frame.h" 8 | #include "populate_frames.h" 9 | #endif 10 | #endif 11 | 12 | #if PY_VERSION_HEX >= 0x030B0000 && !defined(RPYTHON_VMPROF) /* >= 3.11 */ 13 | int vmp_walk_and_record_stack(_PyInterpreterFrame * frame, void **data, 14 | int max_depth, int signal, intptr_t pc); 15 | #else 16 | int vmp_walk_and_record_stack(PY_STACK_FRAME_T * frame, void **data, 17 | int max_depth, int signal, intptr_t pc); 18 | #endif 19 | 20 | int vmp_native_enabled(void); 21 | int vmp_native_enable(void); 22 | int vmp_ignore_ip(intptr_t ip); 23 | int vmp_binary_search_ranges(intptr_t ip, intptr_t * l, int count); 24 | int vmp_native_symbols_read(void); 25 | void vmp_profile_lines(int); 26 | int vmp_profiles_python_lines(void); 27 | 28 | int vmp_ignore_symbol_count(void); 29 | intptr_t * vmp_ignore_symbols(void); 30 | void vmp_set_ignore_symbols(intptr_t * symbols, int count); 31 | void vmp_native_disable(void); 32 | 33 | #ifdef __unix__ 34 | int vmp_read_vmaps(const char * fname); 35 | #endif 36 | -------------------------------------------------------------------------------- /src/vmprof.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _GNU_SOURCE 1 4 | 5 | #ifndef RPYTHON_VMPROF 6 | #include 7 | #endif 8 | 9 | #ifdef VMPROF_UNIX 10 | #include 11 | #endif 12 | 13 | // common defines 14 | #define MARKER_STACKTRACE '\x01' 15 | #define MARKER_VIRTUAL_IP '\x02' 16 | #define MARKER_TRAILER '\x03' 17 | #define MARKER_INTERP_NAME '\x04' /* deprecated */ 18 | #define MARKER_HEADER '\x05' 19 | #define MARKER_TIME_N_ZONE '\x06' 20 | #define MARKER_META '\x07' 21 | #define MARKER_NATIVE_SYMBOLS '\x08' 22 | 23 | #define VERSION_BASE '\x00' 24 | #define VERSION_THREAD_ID '\x01' 25 | #define VERSION_TAG '\x02' 26 | #define VERSION_MEMORY '\x03' 27 | #define VERSION_MODE_AWARE '\x04' 28 | #define VERSION_DURATION '\x05' 29 | #define VERSION_TIMESTAMP '\x06' 30 | 31 | #define PROFILE_MEMORY '\x01' 32 | #define PROFILE_LINES '\x02' 33 | #define PROFILE_NATIVE '\x04' 34 | #define PROFILE_RPYTHON '\x08' 35 | #define PROFILE_REAL_TIME '\x10' 36 | 37 | #define DYN_JIT_FLAG 0xbeefbeef 38 | 39 | #ifdef _WIN32 40 | #ifndef VMPROF_WINDOWS 41 | #define VMPROF_WINDOWS 42 | #endif 43 | #endif 44 | 45 | #ifdef __x86_64__ 46 | #define X86_64 47 | #elif defined(__i386__) 48 | #define X86_32 49 | #endif 50 | 51 | #ifdef VMPROF_UNIX 52 | #if defined(X86_64) || defined(X86_32) || defined(__powerpc64__) 53 | #define VMP_SUPPORTS_NATIVE_PROFILING 54 | #endif 55 | #endif 56 | 57 | #ifdef RPYTHON_VMPROF 58 | // only for pypy 59 | #include "rvmprof.h" 60 | #include "vmprof_stack.h" 61 | #define PY_STACK_FRAME_T vmprof_stack_t 62 | #define PY_EVAL_RETURN_T void 63 | #define PY_THREAD_STATE_T void 64 | #define FRAME_STEP(f) f->next 65 | #define FRAME_CODE(f) f-> 66 | int IS_VMPROF_EVAL(void * ptr); 67 | #else 68 | #define RPY_EXTERN 69 | // for cpython 70 | #include "_vmprof.h" 71 | #include 72 | #include 73 | #include 74 | #define PY_STACK_FRAME_T PyFrameObject 75 | #define PY_EVAL_RETURN_T PyObject 76 | #define PY_THREAD_STATE_T PyThreadState 77 | #define FRAME_STEP(f) f->f_back 78 | 79 | #if PY_VERSION_HEX < 0x030900B1 80 | static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) 81 | { 82 | Py_INCREF(frame->f_code); 83 | return frame->f_code; 84 | } 85 | #endif 86 | 87 | #define FRAME_CODE(f) PyFrame_GetCode(f) 88 | 89 | #if CPYTHON_HAS_FRAME_EVALUATION 90 | #define IS_VMPROF_EVAL(PTR) PTR == (void*)_PyEval_EvalFrameDefault 91 | #else 92 | #define IS_VMPROF_EVAL(PTR) (PTR == (void*)PyEval_EvalFrameEx || PTR == (void*)PyEval_EvalFrame) 93 | #endif 94 | 95 | #endif 96 | 97 | void set_current_codes(void * to); 98 | int opened_profile(const char *interp_name, int memory, int proflines, int native, int real_time); 99 | void flush_codes(void); 100 | 101 | -------------------------------------------------------------------------------- /src/vmprof_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmprof.h" 4 | #include "machine.h" 5 | #include "compat.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef VMPROF_UNIX 12 | #include 13 | #include "vmprof_mt.h" 14 | #include 15 | #include 16 | #endif 17 | 18 | #ifdef VMPROF_UNIX 19 | #include "vmprof_getpc.h" 20 | #endif 21 | 22 | #ifdef VMPROF_LINUX 23 | #include 24 | #endif 25 | 26 | #ifdef VMPROF_BSD 27 | #include 28 | #endif 29 | 30 | #define MAX_FUNC_NAME 1024 31 | 32 | #ifdef VMPROF_UNIX 33 | 34 | ssize_t search_thread(pthread_t tid, ssize_t i); 35 | ssize_t insert_thread(pthread_t tid, ssize_t i); 36 | ssize_t remove_thread(pthread_t tid, ssize_t i); 37 | ssize_t remove_threads(void); 38 | 39 | #endif 40 | 41 | #define MAX_STACK_DEPTH \ 42 | ((SINGLE_BUF_SIZE - sizeof(struct prof_stacktrace_s)) / sizeof(void *)) 43 | 44 | /* 45 | * NOTE SHOULD NOT BE DONE THIS WAY. Here is an example why: 46 | * assume the following struct content: 47 | * struct ... { 48 | * char padding[sizeof(long) - 1]; 49 | * char marker; 50 | * long count, depth; 51 | * void *stack[]; 52 | * } 53 | * 54 | * Here a table of the offsets on a 64 bit machine: 55 | * field | GCC | VSC (windows) 56 | * --------------------------- 57 | * marker | 7 | 3 58 | * count | 8 | 4 59 | * depth | 16 | 8 60 | * stack | 24 | 16 (VSC adds 4 padding byte hurray!) 61 | * 62 | * This means that win32 worked by chance (because sizeof(void*) 63 | * is 4, but fails on win32 64 | */ 65 | typedef struct prof_stacktrace_s { 66 | #ifdef VMPROF_WINDOWS 67 | // if padding is 8 bytes, then on both 32bit and 64bit, the 68 | // stack field is aligned 69 | char padding[sizeof(void*) - 1]; 70 | #else 71 | char padding[sizeof(long) - 1]; 72 | #endif 73 | char marker; 74 | long count, depth; 75 | void *stack[]; 76 | } prof_stacktrace_s; 77 | 78 | #define SIZEOF_PROF_STACKTRACE sizeof(long)+sizeof(long)+sizeof(char) 79 | 80 | RPY_EXTERN 81 | char *vmprof_init(int fd, double interval, int memory, 82 | int proflines, const char *interp_name, int native, int real_time); 83 | 84 | int opened_profile(const char *interp_name, int memory, int proflines, int native, int real_time); 85 | 86 | #ifdef RPYTHON_VMPROF 87 | PY_STACK_FRAME_T *get_vmprof_stack(void); 88 | RPY_EXTERN 89 | intptr_t vmprof_get_traceback(void *stack, void *ucontext, 90 | void **result_p, intptr_t result_length); 91 | #endif 92 | 93 | int vmprof_get_signal_type(void); 94 | long vmprof_get_prepare_interval_usec(void); 95 | long vmprof_get_profile_interval_usec(void); 96 | void vmprof_set_prepare_interval_usec(long value); 97 | void vmprof_set_profile_interval_usec(long value); 98 | int vmprof_is_enabled(void); 99 | void vmprof_set_enabled(int value); 100 | int vmprof_get_itimer_type(void); 101 | #ifdef VMPROF_UNIX 102 | int broadcast_signal_for_threads(void); 103 | int is_main_thread(void); 104 | #endif 105 | -------------------------------------------------------------------------------- /src/vmprof_config.h: -------------------------------------------------------------------------------- 1 | #if !defined(__OpenBSD__) 2 | # define HAVE_SYS_UCONTEXT_H 3 | #else 4 | # define HAVE_SIGNAL_H 5 | #endif 6 | 7 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) 8 | #ifdef __i386__ 9 | #define PC_FROM_UCONTEXT uc_mcontext.mc_eip 10 | #else 11 | #define PC_FROM_UCONTEXT uc_mcontext.mc_rip 12 | #endif 13 | #elif defined(__OpenBSD__) 14 | #define PC_FROM_UCONTEXT sc_rip 15 | #elif defined( __APPLE__) 16 | #if ((ULONG_MAX) == (UINT_MAX)) 17 | #define PC_FROM_UCONTEXT uc_mcontext->__ss.__eip 18 | #else 19 | #if defined(__arm__) || defined(__arm64__) 20 | #define PC_FROM_UCONTEXT uc_mcontext->__ss.__pc 21 | #else 22 | #define PC_FROM_UCONTEXT uc_mcontext->__ss.__rip 23 | #endif 24 | #endif 25 | #elif defined(__arm__) 26 | #define PC_FROM_UCONTEXT uc_mcontext.arm_ip 27 | #elif defined(__linux) && defined(__i386) && defined(__GNUC__) 28 | #define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_EIP] 29 | #elif defined(__s390x__) 30 | #define PC_FROM_UCONTEXT uc_mcontext.psw.addr 31 | #elif defined(__aarch64__) 32 | #define PC_FROM_UCONTEXT uc_mcontext.pc 33 | #elif defined(__powerpc64__) 34 | #define PC_FROM_UCONTEXT uc_mcontext.gp_regs[PT_NIP] 35 | #elif defined(__riscv) 36 | #define PC_FROM_UCONTEXT uc_mcontext.__gregs[REG_PC] 37 | #else 38 | /* linux, gnuc */ 39 | #define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_RIP] 40 | #endif 41 | -------------------------------------------------------------------------------- /src/vmprof_get_custom_offset.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef PYPY_JIT_CODEMAP 4 | #include "vmprof.h" 5 | 6 | #ifdef VMPROF_WINDOWS 7 | #include "msiinttypes/stdint.h" 8 | #else 9 | #include 10 | #endif 11 | 12 | void *pypy_find_codemap_at_addr(long addr, long *start_addr); 13 | long pypy_yield_codemap_at_addr(void *codemap_raw, long addr, 14 | long *current_pos_addr); 15 | 16 | #define MAX_INLINE_DEPTH 384 17 | 18 | long vmprof_write_header_for_jit_addr(intptr_t *result, long n, 19 | intptr_t addr, int max_depth) 20 | { 21 | void *codemap; 22 | long current_pos = 0; 23 | intptr_t ident, local_stack[MAX_INLINE_DEPTH]; 24 | long m; 25 | long start_addr = 0; 26 | 27 | codemap = pypy_find_codemap_at_addr(addr, &start_addr); 28 | if (codemap == NULL || n >= max_depth - 2) 29 | // not a jit code at all or almost max depth 30 | return n; 31 | 32 | // modify the last entry to point to start address and not the random one 33 | // in the middle 34 | result[n++] = VMPROF_ASSEMBLER_TAG; 35 | result[n++] = start_addr; 36 | 37 | // build the list of code idents corresponding to the current 38 | // position inside this particular piece of assembler. If (very 39 | // unlikely) we get more than MAX_INLINE_DEPTH recursion levels 40 | // all inlined inside this single piece of assembler, then stop: 41 | // there will be some missing frames then. Otherwise, we need to 42 | // first collect 'local_stack' and then write it to 'result' in the 43 | // opposite order, stopping at 'max_depth'. Previous versions of 44 | // the code would write the oldest calls and then stop---whereas 45 | // what we really need it to write the newest calls and then stop. 46 | m = 0; 47 | while (m < MAX_INLINE_DEPTH) { 48 | ident = pypy_yield_codemap_at_addr(codemap, addr, ¤t_pos); 49 | if (ident == -1) 50 | // finish 51 | break; 52 | if (ident == 0) 53 | continue; // not main codemap 54 | local_stack[m++] = ident; 55 | } 56 | while (m > 0 && n < max_depth) { 57 | result[n++] = VMPROF_JITTED_TAG; 58 | result[n++] = local_stack[--m]; 59 | } 60 | return n; 61 | } 62 | #endif 63 | -------------------------------------------------------------------------------- /src/vmprof_memory.c: -------------------------------------------------------------------------------- 1 | #include "vmprof_memory.h" 2 | 3 | #ifdef VMPROF_APPLE 4 | /* On OS X we can get RSS using the Mach API. */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static mach_port_t mach_task; 11 | #elif defined(VMPROF_UNIX) 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | /* On '''normal''' Unices we can get RSS from '/proc//status'. */ 20 | static int proc_file = -1; 21 | #endif 22 | 23 | int setup_rss(void) 24 | { 25 | #ifdef VMPROF_LINUX 26 | char buf[128]; 27 | 28 | sprintf(buf, "/proc/%d/status", getpid()); 29 | proc_file = open(buf, O_RDONLY); 30 | return proc_file; 31 | #elif defined(VMPROF_APPLE) 32 | mach_task = mach_task_self(); 33 | return 0; 34 | #else 35 | return 0; 36 | #endif 37 | } 38 | 39 | int teardown_rss(void) 40 | { 41 | #ifdef VMPROF_LINUX 42 | close(proc_file); 43 | proc_file = -1; 44 | return 0; 45 | #else 46 | return 0; 47 | #endif 48 | } 49 | 50 | long get_current_proc_rss(void) 51 | { 52 | #ifdef VMPROF_LINUX 53 | char buf[1024]; 54 | int i = 0; 55 | 56 | if (lseek(proc_file, 0, SEEK_SET) == -1) 57 | return -1; 58 | if (read(proc_file, buf, 1024) == -1) 59 | return -1; 60 | while (i < 1020) { 61 | if (strncmp(buf + i, "VmRSS:\t", 7) == 0) { 62 | i += 7; 63 | return atoi(buf + i); 64 | } 65 | i++; 66 | } 67 | return -1; 68 | #elif defined(VMPROF_APPLE) 69 | mach_msg_type_number_t out_count = MACH_TASK_BASIC_INFO_COUNT; 70 | mach_task_basic_info_data_t taskinfo = { .resident_size = 0 }; 71 | 72 | kern_return_t error = task_info(mach_task, MACH_TASK_BASIC_INFO, (task_info_t)&taskinfo, &out_count); 73 | if (error == KERN_SUCCESS) { 74 | return (long)(taskinfo.resident_size / 1024); 75 | } else { 76 | return -1; 77 | } 78 | #else 79 | return -1; // not implemented 80 | #endif 81 | } 82 | -------------------------------------------------------------------------------- /src/vmprof_memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int setup_rss(void); 4 | int teardown_rss(void); 5 | long get_current_proc_rss(void); 6 | -------------------------------------------------------------------------------- /src/vmprof_mt.c: -------------------------------------------------------------------------------- 1 | #include "vmprof_mt.h" 2 | /* Support for multithreaded write() operations (implementation) */ 3 | 4 | #include 5 | 6 | #if defined(__i386__) || defined(__amd64__) 7 | static inline void write_fence(void) { asm("" : : : "memory"); } 8 | #else 9 | static inline void write_fence(void) { __sync_synchronize(); } 10 | #endif 11 | 12 | static char volatile profbuf_state[MAX_NUM_BUFFERS]; 13 | static struct profbuf_s *profbuf_all_buffers = NULL; 14 | static int volatile profbuf_write_lock = 2; 15 | static long profbuf_pending_write; 16 | 17 | 18 | static void unprepare_concurrent_bufs(void) 19 | { 20 | if (profbuf_all_buffers != NULL) { 21 | munmap(profbuf_all_buffers, sizeof(struct profbuf_s) * MAX_NUM_BUFFERS); 22 | profbuf_all_buffers = NULL; 23 | } 24 | } 25 | 26 | int prepare_concurrent_bufs(void) 27 | { 28 | assert(sizeof(struct profbuf_s) == 8192); 29 | 30 | unprepare_concurrent_bufs(); 31 | profbuf_all_buffers = mmap(NULL, sizeof(struct profbuf_s) * MAX_NUM_BUFFERS, 32 | PROT_READ | PROT_WRITE, 33 | MAP_PRIVATE | MAP_ANONYMOUS, 34 | -1, 0); 35 | if (profbuf_all_buffers == MAP_FAILED) { 36 | profbuf_all_buffers = NULL; 37 | return -1; 38 | } 39 | memset((char *)profbuf_state, PROFBUF_UNUSED, sizeof(profbuf_state)); 40 | profbuf_write_lock = 0; 41 | profbuf_pending_write = -1; 42 | return 0; 43 | } 44 | 45 | static int _write_single_ready_buffer(int fd, long i) 46 | { 47 | /* Try to write to disk the buffer number 'i'. This function must 48 | only be called while we hold the write lock. */ 49 | assert(profbuf_write_lock != 0); 50 | 51 | if (profbuf_pending_write >= 0) { 52 | /* A partially written buffer is waiting. We'll write the 53 | rest of this buffer now, instead of 'i'. */ 54 | i = profbuf_pending_write; 55 | assert(profbuf_state[i] == PROFBUF_READY); 56 | } 57 | 58 | if (profbuf_state[i] != PROFBUF_READY) { 59 | /* this used to be a race condition: the buffer was written by a 60 | different thread already, nothing to do now */ 61 | return 0; 62 | } 63 | 64 | int err; 65 | struct profbuf_s *p = &profbuf_all_buffers[i]; 66 | ssize_t count = write(fd, p->data + p->data_offset, p->data_size); 67 | if (count == p->data_size) { 68 | profbuf_state[i] = PROFBUF_UNUSED; 69 | profbuf_pending_write = -1; 70 | } 71 | else { 72 | if (count > 0) { 73 | p->data_offset += count; 74 | p->data_size -= count; 75 | } 76 | profbuf_pending_write = i; 77 | if (count < 0) 78 | return -1; 79 | } 80 | return 0; 81 | } 82 | 83 | static void _write_ready_buffers(int fd) 84 | { 85 | long i; 86 | int has_write_lock = 0; 87 | 88 | for (i = 0; i < MAX_NUM_BUFFERS; i++) { 89 | if (profbuf_state[i] == PROFBUF_READY) { 90 | if (!has_write_lock) { 91 | if (!__sync_bool_compare_and_swap(&profbuf_write_lock, 0, 1)) 92 | return; /* can't acquire the write lock, give up */ 93 | has_write_lock = 1; 94 | } 95 | if (_write_single_ready_buffer(fd, i) < 0) 96 | break; 97 | } 98 | } 99 | if (has_write_lock) 100 | profbuf_write_lock = 0; 101 | } 102 | 103 | struct profbuf_s *reserve_buffer(int fd) 104 | { 105 | /* Tries to enter a region of code that fills one buffer. If 106 | successful, returns the profbuf_s. It fails only if the 107 | concurrent buffers are all busy (extreme multithreaded usage). 108 | 109 | This might call write() to emit the data sitting in 110 | previously-prepared buffers. In case of write() error, the 111 | error is ignored but unwritten data stays in the buffers. 112 | */ 113 | long i; 114 | 115 | _write_ready_buffers(fd); 116 | 117 | for (i = 0; i < MAX_NUM_BUFFERS; i++) { 118 | if (profbuf_state[i] == PROFBUF_UNUSED && 119 | __sync_bool_compare_and_swap(&profbuf_state[i], PROFBUF_UNUSED, 120 | PROFBUF_FILLING)) { 121 | struct profbuf_s *p = &profbuf_all_buffers[i]; 122 | p->data_size = 0; 123 | p->data_offset = 0; 124 | return p; 125 | } 126 | } 127 | /* no unused buffer found */ 128 | return NULL; 129 | } 130 | 131 | void commit_buffer(int fd, struct profbuf_s *buf) 132 | { 133 | /* Leaves a region of code that filled 'buf'. 134 | 135 | This might call write() to emit the data now ready. In case of 136 | write() error, the error is ignored but unwritten data stays in 137 | the buffers. 138 | */ 139 | 140 | /* Make sure every thread sees the full content of 'buf' */ 141 | write_fence(); 142 | 143 | /* Then set the 'ready' flag */ 144 | long i = buf - profbuf_all_buffers; 145 | assert(profbuf_state[i] == PROFBUF_FILLING); 146 | profbuf_state[i] = PROFBUF_READY; 147 | 148 | if (!__sync_bool_compare_and_swap(&profbuf_write_lock, 0, 1)) { 149 | /* can't acquire the write lock, ignore */ 150 | } 151 | else { 152 | _write_single_ready_buffer(fd, i); 153 | profbuf_write_lock = 0; 154 | } 155 | } 156 | 157 | void cancel_buffer(struct profbuf_s *buf) 158 | { 159 | long i = buf - profbuf_all_buffers; 160 | assert(profbuf_state[i] == PROFBUF_FILLING); 161 | profbuf_state[i] = PROFBUF_UNUSED; 162 | } 163 | 164 | int shutdown_concurrent_bufs(int fd) 165 | { 166 | /* no signal handler can be running concurrently here, because we 167 | already did vmprof_ignore_signals(1) */ 168 | assert(profbuf_write_lock == 0); 169 | profbuf_write_lock = 2; 170 | 171 | /* last attempt to flush buffers */ 172 | int i; 173 | for (i = 0; i < MAX_NUM_BUFFERS; i++) { 174 | while (profbuf_state[i] == PROFBUF_READY) { 175 | if (_write_single_ready_buffer(fd, i) < 0) 176 | return -1; 177 | } 178 | } 179 | unprepare_concurrent_bufs(); 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /src/vmprof_mt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* Support for multithreaded write() operations */ 3 | 4 | #include "vmprof.h" 5 | 6 | #include 7 | #include 8 | 9 | /* The idea is that we have MAX_NUM_BUFFERS available, all of size 10 | SINGLE_BUF_SIZE. Threads and signal handlers can ask to reserve a 11 | buffer, fill it, and finally "commit" it, at which point its 12 | content is written into the profile file. There is no hard 13 | guarantee about the order in which the committed blocks are 14 | actually written. We do this with two constrains: 15 | 16 | - write() calls should not overlap; only one thread can be 17 | currently calling it. 18 | 19 | - the code needs to be multithread-safe *and* signal-handler-safe, 20 | which means it must be written in a wait-free style: never have 21 | spin loops waiting for some lock to be released, from any of 22 | the functions that can be called from the signal handler! The 23 | code holding the lock could be running in the same thread, 24 | currently interrupted by the signal handler. 25 | 26 | The value of MAX_NUM_BUFFERS is a trade-off between too high 27 | (lots of unnecessary memory, lots of checking all of them) 28 | and too low (risk that there is none left). 29 | */ 30 | #define MAX_NUM_BUFFERS 20 31 | 32 | #ifndef MAP_ANONYMOUS 33 | #define MAP_ANONYMOUS MAP_ANON 34 | #endif 35 | 36 | #define PROFBUF_UNUSED 0 37 | #define PROFBUF_FILLING 1 38 | #define PROFBUF_READY 2 39 | 40 | 41 | struct profbuf_s { 42 | unsigned int data_size; 43 | unsigned int data_offset; 44 | char data[SINGLE_BUF_SIZE]; 45 | }; 46 | 47 | int prepare_concurrent_bufs(void); 48 | struct profbuf_s *reserve_buffer(int fd); 49 | void commit_buffer(int fd, struct profbuf_s *buf); 50 | void cancel_buffer(struct profbuf_s *buf); 51 | int shutdown_concurrent_bufs(int fd); 52 | -------------------------------------------------------------------------------- /src/vmprof_unix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* VMPROF 4 | * 5 | * statistical sampling profiler specifically designed to profile programs 6 | * which run on a Virtual Machine and/or bytecode interpreter, such as Python, 7 | * etc. 8 | * 9 | * The logic to dump the C stack traces is partly stolen from the code in 10 | * gperftools. 11 | * The file "getpc.h" has been entirely copied from gperftools. 12 | * 13 | * Tested only on gcc, linux, x86_64. 14 | * 15 | * Copyright (C) 2014-2017 16 | * Antonio Cuni - anto.cuni@gmail.com 17 | * Maciej Fijalkowski - fijall@gmail.com 18 | * Armin Rigo - arigo@tunes.org 19 | * Richard Plangger - planrichi@gmail.com 20 | * 21 | */ 22 | 23 | #include "vmprof.h" 24 | 25 | #include "vmprof_mt.h" 26 | 27 | #ifdef __FreeBSD__ 28 | #include 29 | #endif 30 | #include 31 | 32 | RPY_EXTERN void vmprof_ignore_signals(int ignored); 33 | RPY_EXTERN long vmprof_enter_signal(void); 34 | RPY_EXTERN long vmprof_exit_signal(void); 35 | 36 | /* ************************************************************* 37 | * functions to dump the stack trace 38 | * ************************************************************* 39 | */ 40 | 41 | #ifndef RPYTHON_VMPROF 42 | PY_THREAD_STATE_T * _get_pystate_for_this_thread(void); 43 | #endif 44 | int get_stack_trace(PY_THREAD_STATE_T * current, void** result, int max_depth, intptr_t pc); 45 | 46 | /* ************************************************************* 47 | * the signal handler 48 | * ************************************************************* 49 | */ 50 | 51 | #include 52 | 53 | void segfault_handler(int arg); 54 | int _vmprof_sample_stack(struct profbuf_s *p, PY_THREAD_STATE_T * tstate, ucontext_t * uc); 55 | void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext); 56 | 57 | 58 | /* ************************************************************* 59 | * the setup and teardown functions 60 | * ************************************************************* 61 | */ 62 | 63 | int install_sigprof_handler(void); 64 | int remove_sigprof_handler(void); 65 | int install_sigprof_timer(void); 66 | int remove_sigprof_timer(void); 67 | void atfork_disable_timer(void); 68 | void atfork_enable_timer(void); 69 | void atfork_close_profile_file(void); 70 | int install_pthread_atfork_hooks(void); 71 | 72 | #ifdef VMP_SUPPORTS_NATIVE_PROFILING 73 | void init_cpyprof(int native); 74 | static void disable_cpyprof(void); 75 | #endif 76 | 77 | int close_profile(void); 78 | 79 | RPY_EXTERN 80 | int vmprof_enable(int memory, int native, int real_time); 81 | RPY_EXTERN 82 | int vmprof_disable(void); 83 | RPY_EXTERN 84 | int vmprof_register_virtual_function(char *code_name, intptr_t code_uid, 85 | int auto_retry); 86 | 87 | 88 | void vmprof_aquire_lock(void); 89 | void vmprof_release_lock(void); 90 | -------------------------------------------------------------------------------- /src/vmprof_win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "windows.h" 4 | #include "compat.h" 5 | #include "vmp_stack.h" 6 | #include 7 | 8 | int prepare_concurrent_bufs(void); 9 | 10 | // This file has been inspired (but not copied from since the LICENSE 11 | // would not allow it) from verysleepy profiler 12 | 13 | int vmp_write_all(const char *buf, size_t bufsize); 14 | 15 | #ifdef RPYTHON_VMPROF 16 | typedef struct pypy_threadlocal_s PY_WIN_THREAD_STATE; 17 | #else 18 | typedef PyThreadState PY_WIN_THREAD_STATE; 19 | #endif 20 | 21 | 22 | int vmprof_register_virtual_function(char *code_name, intptr_t code_uid, 23 | int auto_retry); 24 | 25 | PY_WIN_THREAD_STATE * get_current_thread_state(void); 26 | int vmprof_enable(int memory, int native, int real_time); 27 | int vmprof_disable(void); 28 | void vmprof_ignore_signals(int ignored); 29 | int vmp_native_enable(void); 30 | void vmp_native_disable(void); 31 | int get_stack_trace(PY_WIN_THREAD_STATE * current, void** result, 32 | int max_depth, intptr_t pc); 33 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | six 2 | requests 3 | urllib3==1.26.6 ; python_version < '3.9' 4 | backports.shutil_which 5 | cffi 6 | pytest 7 | hypothesis 8 | colorama 9 | pytz 10 | attrs 11 | py 12 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py34, py35, pypy 3 | 4 | 5 | [testenv] 6 | deps= 7 | pytest 8 | cffi 9 | commands=py.test vmprof/test/ 10 | -------------------------------------------------------------------------------- /vmprof/__main__.py: -------------------------------------------------------------------------------- 1 | import runpy 2 | import platform 3 | import sys, os 4 | import tempfile 5 | import vmprof 6 | from vmshare.service import Service 7 | try: 8 | import _jitlog 9 | except ImportError: 10 | _jitlog = None 11 | 12 | OUTPUT_CLI = 'cli' 13 | OUTPUT_WEB = 'web' 14 | OUTPUT_FILE = 'file' 15 | 16 | def show_stats(filename, output_mode, args): 17 | if output_mode == OUTPUT_FILE: 18 | return 19 | elif output_mode == OUTPUT_CLI: 20 | stats = vmprof.read_profile(filename) 21 | vmprof.cli.show(stats) 22 | elif output_mode == OUTPUT_WEB: 23 | host, auth = args.web_url, args.web_auth 24 | service = Service(host, auth) 25 | service.post({ Service.FILE_CPU_PROFILE: filename, 26 | Service.FILE_JIT_PROFILE: filename + '.jit', 27 | 'argv': ' '.join(sys.argv[:]), 28 | 'VM': platform.python_implementation() }) 29 | 30 | def main(): 31 | args = vmprof.cli.parse_args(sys.argv[1:]) 32 | 33 | # None means default on this platform 34 | native = None 35 | if args.no_native: 36 | native = False 37 | if args.web: 38 | output_mode = OUTPUT_WEB 39 | elif args.output: 40 | output_mode = OUTPUT_FILE 41 | else: 42 | output_mode = OUTPUT_CLI 43 | 44 | if output_mode == OUTPUT_FILE: 45 | prof_file = args.output 46 | prof_name = prof_file.name 47 | else: 48 | prof_file = tempfile.NamedTemporaryFile(delete=False) 49 | prof_name = prof_file.name 50 | 51 | 52 | vmprof.enable(prof_file.fileno(), args.period, args.mem, 53 | args.lines, native=native) 54 | if args.jitlog and _jitlog: 55 | fd = os.open(prof_name + '.jit', os.O_WRONLY | os.O_TRUNC | os.O_CREAT) 56 | _jitlog.enable(fd) 57 | # invoke the user program: 58 | try: 59 | sys.argv[1:] = args.args 60 | if args.module: 61 | runpy.run_module(args.program, run_name='__main__', alter_sys=True) 62 | else: 63 | sys.path.insert(0, os.path.dirname(args.program)) 64 | runpy.run_path(args.program, run_name='__main__') 65 | except BaseException as e: 66 | if not isinstance(e, (KeyboardInterrupt, SystemExit)): 67 | raise 68 | # 69 | vmprof.disable() 70 | if args.jitlog and _jitlog: 71 | _jitlog.disable() 72 | 73 | prof_file.close() 74 | show_stats(prof_name, output_mode, args) 75 | if output_mode != OUTPUT_FILE: 76 | os.unlink(prof_name) 77 | 78 | main() 79 | -------------------------------------------------------------------------------- /vmprof/cli.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import argparse 4 | import sys 5 | from six.moves import configparser 6 | 7 | 8 | def build_argparser(): 9 | parser = argparse.ArgumentParser( 10 | description='VMprof', 11 | prog="vmprof" 12 | ) 13 | parser.add_argument( 14 | '-m', 15 | dest='module', 16 | action='store_true', 17 | help='Run module as a script' 18 | ) 19 | parser.add_argument( 20 | 'program', 21 | help='program' 22 | ) 23 | 24 | parser.add_argument( 25 | 'args', 26 | nargs=argparse.REMAINDER, 27 | help='program arguments' 28 | ) 29 | 30 | parser.add_argument( 31 | '--config', 32 | type=argparse.FileType('r'), 33 | ) 34 | 35 | parser.add_argument( 36 | '--period', '-p', 37 | type=float, 38 | default=0.00099, 39 | help='Sampling period (in seconds)' 40 | ) 41 | 42 | parser.add_argument( 43 | '--web-auth', 44 | help='Authtoken for your acount on the server, works only when --web is used' 45 | ) 46 | 47 | parser.add_argument( 48 | '--web-url', 49 | metavar='url', 50 | default='http://vmprof.com', 51 | help='Provide URL instead of the default vmprof.com)' 52 | ) 53 | parser.add_argument( 54 | '--mem', 55 | action="store_true", 56 | help='Do memory profiling as well', 57 | ) 58 | parser.add_argument( 59 | '--lines', 60 | action="store_true", 61 | help='Store lines execution stats', 62 | ) 63 | parser.add_argument( 64 | '--jitlog', 65 | action='store_true', 66 | help='Upload the jitlog to remote server (defaults to vmprof.com)', 67 | ) 68 | parser.add_argument( 69 | '--no-native', '-n', 70 | default=not sys.platform.startswith('linux'), 71 | action='store_true', 72 | help='Disable native profiling for this run' 73 | ) 74 | output_mode_args = parser.add_mutually_exclusive_group() 75 | output_mode_args.add_argument( 76 | '--web', 77 | action='store_true', 78 | help='Upload profiling stats to a remote server (defaults to vmprof.com)' 79 | ) 80 | output_mode_args.add_argument( 81 | '--output', '-o', 82 | metavar='file.prof', 83 | type=argparse.FileType('w+b'), 84 | help='Save profiling data to file' 85 | ) 86 | 87 | return parser 88 | 89 | 90 | def parse_args(argv): 91 | parser = build_argparser() 92 | args = parser.parse_args(argv) 93 | if args.config: 94 | ini_options = [ 95 | ('period', float), 96 | ('web', str), 97 | ('mem', bool), 98 | ('web-auth', str), 99 | ('web-url', str), 100 | ('output', str), 101 | ('no-native', bool), 102 | ] 103 | 104 | ini_parser = IniParser(args.config) 105 | 106 | for name, type in ini_options: 107 | argname = name.replace("-", "_") 108 | default = parser.get_default(argname) 109 | current = getattr(args, argname, default) 110 | if current == default: 111 | value = ini_parser.get_option(name, type, default) 112 | setattr(args, argname, value) 113 | 114 | return args 115 | 116 | 117 | class IniParser(object): 118 | 119 | def __init__(self, f): 120 | self.ini_parser = configparser.ConfigParser() 121 | self.ini_parser.readfp(f) 122 | 123 | def get_option(self, name, type, default=None): 124 | if type == float: 125 | try: 126 | return self.ini_parser.getfloat('global', name) 127 | except configparser.NoOptionError: 128 | return default 129 | elif type == bool: 130 | try: 131 | return self.ini_parser.getboolean('global', name) 132 | except configparser.NoOptionError: 133 | return default 134 | 135 | try: 136 | return self.ini_parser.get('global', name) 137 | except configparser.NoOptionError: 138 | return default 139 | 140 | 141 | def show(stats): 142 | p = stats.top_profile() 143 | if not p: 144 | print("No stats, profile is empty!\nDid you program run long enough (more than 1 second)?") 145 | return 146 | 147 | p.sort(key=lambda x: x[1], reverse=True) 148 | top = p[0][1] 149 | 150 | max_len = max([_namelen(e[0]) for e in p]) 151 | 152 | print(" vmprof output:") 153 | print(" %: name:" + " " * (max_len - 3) + "location:") 154 | 155 | for k, v in p: 156 | v = "%.1f%%" % (float(v) / top * 100) 157 | if v == '0.0%': 158 | v = '<0.1%' 159 | if k.startswith('py:'): 160 | try: 161 | _, func_name, lineno, filename = k.split(":", 3) 162 | lineno = int(lineno) 163 | except ValueError: 164 | print(" %s %s" % (v.ljust(7), k.ljust(max_len + 1))) 165 | # badly done split 166 | else: 167 | print(" %s %s %s:%d" % (v.ljust(7), func_name.ljust(max_len + 1), filename, lineno)) 168 | else: 169 | print(" %s %s" % (v.ljust(7), k.ljust(max_len + 1))) 170 | 171 | 172 | def _namelen(e): 173 | if e.startswith('py:'): 174 | return len(e.split(':')[1]) 175 | else: 176 | return len(e) 177 | -------------------------------------------------------------------------------- /vmprof/profiler.py: -------------------------------------------------------------------------------- 1 | import vmprof 2 | import tempfile 3 | 4 | from vmprof.stats import Stats 5 | from vmprof.reader import _read_prof 6 | 7 | 8 | class VMProfError(Exception): 9 | pass 10 | 11 | class ProfilerContext(object): 12 | done = False 13 | 14 | def __init__(self, name, period, memory, native, real_time): 15 | if name is None: 16 | self.tmpfile = tempfile.NamedTemporaryFile("w+b", delete=False) 17 | else: 18 | self.tmpfile = open(name, "w+b") 19 | self.filename = self.tmpfile.name 20 | self.period = period 21 | self.memory = memory 22 | self.native = native 23 | self.real_time = real_time 24 | 25 | def __enter__(self): 26 | vmprof.enable(self.tmpfile.fileno(), self.period, self.memory, 27 | native=self.native, real_time=self.real_time) 28 | 29 | def __exit__(self, type, value, traceback): 30 | vmprof.disable() 31 | self.tmpfile.close() # flushes the stream 32 | self.done = True 33 | 34 | 35 | def read_profile(prof_file): 36 | file_to_close = None 37 | if not hasattr(prof_file, 'read'): 38 | prof_file = file_to_close = open(str(prof_file), 'rb') 39 | 40 | state = _read_prof(prof_file) 41 | 42 | if file_to_close: 43 | file_to_close.close() 44 | 45 | jit_frames = {} 46 | d = dict(state.virtual_ips) 47 | s = Stats(state.profiles, d, jit_frames, interp=state.interp_name, 48 | start_time=state.start_time, end_time=state.end_time, 49 | meta=state.meta, state=state) 50 | return s 51 | 52 | 53 | class Profiler(object): 54 | ctx = None 55 | 56 | def __init__(self): 57 | self._lib_cache = {} 58 | 59 | def measure(self, name=None, period=0.001, memory=False, native=False, real_time=False): 60 | self.ctx = ProfilerContext(name, period, memory, native, real_time) 61 | return self.ctx 62 | 63 | def get_stats(self): 64 | if not self.ctx: 65 | raise VMProfError("no profiling done") 66 | if not self.ctx.done: 67 | raise VMProfError("profiling in process") 68 | res = read_profile(self.ctx.tmpfile.name) 69 | self.ctx = None 70 | return res 71 | 72 | -------------------------------------------------------------------------------- /vmprof/test/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import _vmprof 3 | 4 | def enable(): 5 | pass 6 | 7 | def disable(): 8 | pass 9 | -------------------------------------------------------------------------------- /vmprof/test/cpuburn.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from time import time 4 | import vmprof 5 | 6 | 7 | class Burner: 8 | 9 | ITER_N = 100000 10 | 11 | def __init__(self): 12 | self._rand = 123 13 | self._current = self._next_rand() 14 | 15 | def _next_rand(self): 16 | # http://rosettacode.org/wiki/Linear_congruential_generator 17 | self._rand = (1103515245 * self._rand + 12345) & 0x7fffffff 18 | return self._rand 19 | 20 | def _iterate(self, n): 21 | started = time() 22 | for i in range(n): 23 | self._current ^= self._next_rand() 24 | ended = time() 25 | return float(ended - started) * 1000. 26 | 27 | def burn(self, ms): 28 | done = 0 29 | iters = 0 30 | while done < ms: 31 | done += self._iterate(self.ITER_N) 32 | iters += 1 33 | return iters * self.ITER_N, iters, done 34 | 35 | 36 | def test(): 37 | RUN_MS = 1000 38 | RUNTIME = 30 39 | 40 | print("Running for {} seconds ..\n".format(RUNTIME)) 41 | 42 | b = Burner() 43 | for i in range(RUNTIME): 44 | t, i, d = b.burn(RUN_MS) 45 | print("Actual run-time: {} / Requested run-time: {}, {} iterations. Total iterations: {}".format(d, RUN_MS, i, t)) 46 | 47 | 48 | if __name__ == '__main__': 49 | 50 | PROFILE_FILE = 'vmprof_cpuburn.dat' 51 | 52 | flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC 53 | if sys.platform == 'win32': 54 | flags |= os.O_BINARY 55 | outfd = os.open(PROFILE_FILE, flags) 56 | vmprof.enable(outfd, period=0.01) 57 | test() 58 | vmprof.disable() 59 | 60 | print("\nProfile written to {}.".format(PROFILE_FILE)) 61 | print("To view the profile, run: vmprofshow {} tree".format(PROFILE_FILE)) 62 | -------------------------------------------------------------------------------- /vmprof/test/richards.cpython.prof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/vmprof/test/richards.cpython.prof -------------------------------------------------------------------------------- /vmprof/test/richards.pypy.prof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/vmprof/test/richards.pypy.prof -------------------------------------------------------------------------------- /vmprof/test/simple_nested.pypy.prof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/vmprof/test/simple_nested.pypy.prof -------------------------------------------------------------------------------- /vmprof/test/simple_nested.pypy.prof.libcache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/vmprof/test/simple_nested.pypy.prof.libcache -------------------------------------------------------------------------------- /vmprof/test/sqlite.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import math 3 | from os import unlink 4 | from os.path import exists 5 | 6 | 7 | def main(): 8 | if exists('test.db'): 9 | unlink('test.db') 10 | conn = sqlite3.connect('test.db') 11 | 12 | c = conn.cursor() 13 | c.execute(""" 14 | CREATE TABLE stocks 15 | (date text, trans text, symbol text, qty real, price real) 16 | """) 17 | for i in range(10000): 18 | c.execute(""" 19 | INSERT INTO stocks VALUES ('{}','{}','{}', {}, {}) 20 | """.format('2011-01-05', 'BUY', 'ABC', i, math.sqrt(i))) 21 | 22 | 23 | main() 24 | -------------------------------------------------------------------------------- /vmprof/test/test_c_source.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import sys 3 | import pytest 4 | import vmprof 5 | from cffi import FFI 6 | from array import array 7 | 8 | IS_PYPY = '__pypy__' in sys.builtin_module_names 9 | 10 | sample = None 11 | @pytest.mark.skipif("sys.platform == 'win32' or IS_PYPY") 12 | class TestStack(object): 13 | def setup_class(cls): 14 | stack_ffi = FFI() 15 | stack_ffi.cdef(""" 16 | typedef intptr_t ptr_t; 17 | int vmp_binary_search_ranges(ptr_t ip, ptr_t * l, int count); 18 | int vmp_ignore_ip(ptr_t ip); 19 | int vmp_ignore_symbol_count(void); 20 | ptr_t * vmp_ignore_symbols(void); 21 | void vmp_set_ignore_symbols(ptr_t * symbols, int count); 22 | int vmp_read_vmaps(const char * fname); 23 | void vmp_native_disable(); 24 | """) 25 | with open("src/vmp_stack.c", "rb") as fd: 26 | source = fd.read().decode() 27 | libs = [] #['unwind', 'unwind-x86_64'] 28 | if sys.platform.startswith('linux'): 29 | libs = ['unwind'] 30 | if platform.machine() == 'x86_64': 31 | libs.append('unwind-x86_64') 32 | # trick: compile with _CFFI_USE_EMBEDDING=1 which will not define Py_LIMITED_API 33 | sources = [] 34 | if sys.version_info[:2] == (3,11): 35 | sources += ['src/populate_frames.c']# needed for cp311 but must not be included in py < 3.11 36 | stack_ffi.set_source("vmprof.test._test_stack", source, include_dirs=['src'], 37 | define_macros=[('_CFFI_USE_EMBEDDING',1), ('PY_TEST',1), 38 | ('VMP_SUPPORTS_NATIVE_PROFILING',1)], 39 | libraries=libs, extra_compile_args=['-Werror', '-g'], sources=sources) 40 | 41 | stack_ffi.compile(verbose=True) 42 | from vmprof.test import _test_stack as clib 43 | cls.lib = clib.lib 44 | cls.ffi = clib.ffi 45 | 46 | def test_binary_search_ranges(self): 47 | a = array('l', [1,2,10,20,30,40]) 48 | count = len(a) 49 | abuf = self.ffi.from_buffer(a) 50 | a_addr = self.ffi.cast("ptr_t*", abuf) 51 | assert self.lib.vmp_binary_search_ranges(0, a_addr, count) == -1 52 | assert self.lib.vmp_binary_search_ranges(1, a_addr, count) == 0 53 | assert self.lib.vmp_binary_search_ranges(2, a_addr, count) == 0 54 | for i in range(3,10): 55 | assert self.lib.vmp_binary_search_ranges(i, a_addr, count) == 0 56 | for i in range(10,21): 57 | assert self.lib.vmp_binary_search_ranges(i, a_addr, count) == 2 58 | for i in range(21,30): 59 | assert self.lib.vmp_binary_search_ranges(i, a_addr, count) == 2 60 | for i in range(30,41): 61 | assert self.lib.vmp_binary_search_ranges(i, a_addr, count) == 4 62 | assert self.lib.vmp_binary_search_ranges(41, a_addr, count) == -1 63 | 64 | def test_ignore_ip(self): 65 | a = array('l', [1,2,100,150]) 66 | abuf = self.ffi.from_buffer(a) 67 | a_addr = self.ffi.cast("ptr_t*", abuf) 68 | self.lib.vmp_set_ignore_symbols(a_addr, len(a)) 69 | assert self.lib.vmp_ignore_ip(0) == 0 70 | assert self.lib.vmp_ignore_ip(1) == 1 71 | assert self.lib.vmp_ignore_ip(2) == 1 72 | assert self.lib.vmp_ignore_ip(3) == 0 73 | for i in range(3,100): 74 | assert self.lib.vmp_ignore_ip(i) == 0 75 | for i in range(100,151): 76 | assert self.lib.vmp_ignore_ip(i) == 1 77 | assert self.lib.vmp_ignore_ip(151) == 0 78 | self.lib.vmp_set_ignore_symbols(self.ffi.NULL, 0) 79 | 80 | @pytest.mark.skipif("not sys.platform.startswith('linux')") 81 | def test_read_vmaps(self, tmpdir): 82 | lib = self.lib 83 | f1 = tmpdir.join("vmap1") 84 | f1.write("""\ 85 | 00000-11111 x y z w python 86 | 11111-22222 x y z w python site-packages 87 | """) 88 | filename = str(f1).encode('utf-8') 89 | assert lib.vmp_read_vmaps(filename) == 1 90 | 91 | assert lib.vmp_ignore_symbol_count() == 2 92 | symbols = lib.vmp_ignore_symbols() 93 | assert symbols[0] == 0 and symbols[1] == 0x22222 94 | assert lib.vmp_ignore_ip(0x1) == 1 95 | assert lib.vmp_ignore_ip(0x11111) == 1 96 | assert lib.vmp_ignore_ip(0x11112) == 1 97 | assert lib.vmp_ignore_ip(0x22222) == 1 98 | assert lib.vmp_ignore_ip(0x22223) == 0 99 | lib.vmp_native_disable() 100 | 101 | @pytest.mark.skipif("not sys.platform.startswith('linux')") 102 | def test_self_vmaps(self): 103 | lib = self.lib 104 | assert lib.vmp_read_vmaps(b"/proc/self/maps") == 1 105 | 106 | count = lib.vmp_ignore_symbol_count() 107 | assert count >= 2 108 | symbols = lib.vmp_ignore_symbols() 109 | min = -1 110 | for i in range(0, count, 2): 111 | start, end = symbols[i], symbols[i+1] 112 | assert min < start 113 | assert start <= end 114 | min = end 115 | lib.vmp_native_disable() 116 | 117 | 118 | @pytest.mark.skipif("not sys.platform.startswith('darwin')") 119 | def test_read_vmaps_darwin(self): 120 | assert self.lib.vmp_read_vmaps(self.ffi.NULL) == 1 121 | 122 | @pytest.mark.skipif("not sys.platform.startswith('linux')") 123 | def test_overflow_vmaps(self, tmpdir): 124 | lib = self.lib 125 | f1 = tmpdir.join("vmap1") 126 | lines = [] 127 | for l in range(0,10000, 2): 128 | # non overlapping ranges that must be considered! 129 | lines.append('%x-%x x y z w python' % (l,l+1)) 130 | f1.write('\n'.join(lines)) 131 | filename = str(f1).encode('utf-8') 132 | assert lib.vmp_read_vmaps(filename) == 1 133 | for l in range(0, 10000): 134 | assert self.lib.vmp_ignore_ip(l) == 1 135 | assert self.lib.vmp_ignore_ip(10001) == 0 136 | -------------------------------------------------------------------------------- /vmprof/test/test_c_symboltable.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import pytest 4 | from cffi import FFI 5 | from array import array 6 | 7 | import _vmprof 8 | 9 | if sys.platform != 'win32': 10 | 11 | ffi = FFI() 12 | ffi.cdef(""" 13 | //void dump_all_known_symbols(int fd); 14 | int test_extract(char ** name, int * lineno, char ** src); 15 | int test_extract_sofile(char ** name, int * lineno, char ** src); 16 | 17 | int somefunc(); 18 | void* get_somefunc(int); 19 | """) 20 | with open("src/symboltable.c", "rb") as fd: 21 | source = fd.read().decode() 22 | source += """ 23 | 24 | static char gname[64]; 25 | static char gsrc[128]; 26 | int test_extract(char ** name, int * lineno, char ** src) 27 | { 28 | *name = gname; 29 | *src = gsrc; 30 | gname[0] = 0; 31 | gsrc[0] = 0; 32 | return vmp_resolve_addr(&vmp_resolve_addr, gname, 64, 33 | lineno, gsrc, 128); 34 | } 35 | int test_extract_sofile(char ** name, int * lineno, char ** src) 36 | { 37 | *name = gname; 38 | *src = gsrc; 39 | gname[0] = 0; 40 | gsrc[0] = 0; 41 | return vmp_resolve_addr(&abs, gname, 64, 42 | lineno, gsrc, 128); 43 | } 44 | 45 | int somefunc() {return 1;} 46 | void* get_somefunc(int which) { 47 | if (which == 0) return &somefunc; 48 | if (which == 1) return &abs; 49 | return NULL; 50 | } 51 | """ 52 | # replace the name, otherwise we'll get the one built into pypy 53 | source = source.replace('vmp_resolve_addr', 'vmp_resolve_addr_2') 54 | libs = [] #['unwind', 'unwind-x86_64'] 55 | includes = ['src'] 56 | if sys.platform.startswith('linux'): 57 | for src in ["src/libbacktrace/state.c", 58 | "src/libbacktrace/backtrace.c", 59 | "src/libbacktrace/fileline.c", 60 | "src/libbacktrace/posix.c", 61 | "src/libbacktrace/mmap.c", 62 | "src/libbacktrace/mmapio.c", 63 | "src/libbacktrace/elf.c", 64 | "src/libbacktrace/dwarf.c", 65 | "src/libbacktrace/sort.c", 66 | ]: 67 | with open(src, "rb") as fd: 68 | source += fd.read().decode() 69 | includes.append('src/libbacktrace') 70 | 71 | extra_compile = [] 72 | if sys.platform.startswith("linux"): 73 | extra_compile = ['-DVMPROF_LINUX', '-DVMPROF_UNIX', '-Werror', '-g'] 74 | 75 | # trick: compile with _CFFI_USE_EMBEDDING=1 which will not define Py_LIMITED_API 76 | ffi.set_source("vmprof.test._test_symboltable", source, include_dirs=includes, 77 | define_macros=[('_CFFI_USE_EMBEDDING',1),('_PY_TEST',1)], libraries=libs, 78 | extra_compile_args=extra_compile) 79 | 80 | ffi.compile(verbose=True) 81 | 82 | @pytest.mark.skipif("sys.platform == 'win32'") 83 | class TestSymbolTable(object): 84 | def setup_class(cls): 85 | from vmprof.test import _test_symboltable as clib 86 | cls.lib = clib.lib 87 | cls.ffi = clib.ffi 88 | 89 | def test_resolve_addr(self): 90 | lib = self.lib 91 | ffi = self.ffi 92 | name = ffi.new("char**") 93 | src = ffi.new("char**") 94 | _lineno = ffi.new("int*") 95 | lib.test_extract(name, _lineno, src) 96 | 97 | assert ffi.string(name[0]) == b"vmp_resolve_addr_2" 98 | srcfile = ffi.string(src[0]) 99 | assert b"_test_symboltable" in srcfile 100 | if not srcfile.endswith(b"vmprof/test/_test_symboltable.c"): 101 | pytest.skip("couldn't determine source file, but shared library name seemed ok") 102 | # lines are not included in stab 103 | if sys.platform.startswith('linux'): 104 | with open("vmprof/test/_test_symboltable.c", "rb") as fd: 105 | lineno = 1 106 | for line in fd.readlines(): 107 | if "int vmp_resolve_addr_2(void * addr," in line.decode(): 108 | if _lineno[0] == lineno: 109 | break 110 | lineno += 1 111 | else: 112 | assert False, "could not match line number" 113 | 114 | def test_sofile_in_srcfile(self): 115 | lib = self.lib 116 | ffi = self.ffi 117 | name = ffi.new("char**") 118 | src = ffi.new("char**") 119 | _lineno = ffi.new("int*") 120 | # the idea of this test is to extract some details out of e.g. libc 121 | # usually linux distros do not ship dwarf information unless you install 122 | # them. 123 | lib.test_extract_sofile(name, _lineno, src) 124 | 125 | assert ffi.string(name[0]) == b"abs" 126 | # should be something like /lib64/libc.so.6 (e.g. on Fedora 25) 127 | if sys.platform.startswith("linux"): 128 | assert b"libc" in ffi.string(src[0]) 129 | assert b".so" in ffi.string(src[0]) 130 | elif sys.platform == "darwin": 131 | # osx 132 | assert b"libsystem_c.dylib" in ffi.string(src[0]) 133 | 134 | @pytest.mark.skipif("not hasattr(_vmprof, 'resolve_addr')") 135 | def test_vmprof_resolve_addr(self): 136 | res = _vmprof.resolve_addr(int(self.ffi.cast('intptr_t', self.lib.get_somefunc(0)))) 137 | assert res[0] == 'somefunc' 138 | 139 | def test_vmprof_resolve_many_addr(self): 140 | import vmprof 141 | addrs = [int(self.ffi.cast('intptr_t', self.lib.get_somefunc(which))) for which in [0, 1, 2]] 142 | res = vmprof.resolve_many_addr(addrs) 143 | assert len(res) <= 3 144 | if addrs[0] in res: 145 | assert res[addrs[0]][0] == 'somefunc' 146 | if addrs[1] in res: 147 | assert res[addrs[1]][0] == 'abs' 148 | -------------------------------------------------------------------------------- /vmprof/test/test_config.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | 3 | from vmprof import cli 4 | 5 | 6 | def ini_config(content): 7 | _, name = tempfile.mkstemp() 8 | f = open(name, 'w') 9 | f.write(content) 10 | f.close() 11 | return name 12 | 13 | 14 | def test_parser_config(): 15 | test_file = ini_config(""" 16 | [global] 17 | period = 10.0 18 | """) 19 | 20 | args = cli.parse_args([ 21 | '--config', test_file, 22 | 'example.py' 23 | ]) 24 | 25 | assert args.period == 10.0 26 | 27 | 28 | def test_parser_arg_precedence(): 29 | test_file = ini_config(""" 30 | [global] 31 | period = 10.0 32 | web-url = example.com 33 | """) 34 | 35 | args = cli.parse_args([ 36 | '--config', test_file, 37 | '--period', '5.0', 38 | 'example.py' 39 | ]) 40 | 41 | assert args.period == 5.0 42 | assert args.web is False 43 | assert args.web_url == "example.com" 44 | assert args.mem == False 45 | 46 | 47 | def test_parser_without_section(): 48 | 49 | test_file = ini_config(""" 50 | [global] 51 | period = 0.1 52 | web-url=example.com 53 | enable-nonvirtual = False 54 | no-native = True 55 | """) 56 | 57 | args = cli.parse_args([ 58 | '--config', test_file, 59 | 'example.py' 60 | ]) 61 | 62 | assert test_file == args.config.name 63 | assert args.no_native == True 64 | 65 | 66 | def test_parser_with_m_switch(): 67 | args = cli.parse_args(['-m', 'example']) 68 | 69 | assert args.module 70 | assert args.program == 'example' 71 | -------------------------------------------------------------------------------- /vmprof/test/test_reader.py: -------------------------------------------------------------------------------- 1 | 2 | import struct, pytest 3 | from vmprof import reader 4 | from vmprof.reader import (FileReadError, MARKER_HEADER) 5 | from vmprof.test.test_run import (read_one_marker, read_header, 6 | BufferTooSmallError, FileObjWrapper) 7 | 8 | class FileObj(object): 9 | def __init__(self, lst=None): 10 | self.s = b'' 11 | if lst is None: 12 | return 13 | for item in lst: 14 | if isinstance(item, int): 15 | item = struct.pack('l', item) 16 | self.write(item) 17 | 18 | def read(self, count): 19 | if self.s: 20 | s = self.s[:count] 21 | self.s = self.s[count:] 22 | return s # might be incomplete 23 | return b'' 24 | 25 | def write(self, s): 26 | self.s += s 27 | 28 | def tell(self): 29 | return 0 30 | 31 | def test_fileobj(): 32 | f = FileObj() 33 | f.write(b'foo') 34 | f.write(b'bar') 35 | assert f.read(2) == b'fo' 36 | assert f.read(2) == b'ob' 37 | assert f.read(4) == b'ar' 38 | assert f.read(1) == b'' 39 | 40 | def test_fileobj_wrapper(): 41 | f1 = FileObj([b"123456"]) 42 | fw = FileObjWrapper(f1) 43 | assert fw.read(4) == b"1234" 44 | exc = pytest.raises(BufferTooSmallError, fw.read, 4) 45 | f1.write(b"789") 46 | fw = FileObjWrapper(f1, exc.value.get_buf()) 47 | assert fw.read(3) == b'123' 48 | assert fw.read(4) == b'4567' 49 | assert fw.read(2) == b'89' 50 | 51 | -------------------------------------------------------------------------------- /vmprof/test/test_stats.py: -------------------------------------------------------------------------------- 1 | import json 2 | import zlib 3 | 4 | import pytest 5 | import six 6 | 7 | import vmprof 8 | from vmprof.stats import Node, Stats, JittedCode, AssemblerCode 9 | 10 | def test_tree_basic(): 11 | profiles = [([1, 2], 1, 1), 12 | ([1, 2], 1, 1)] 13 | stats = Stats(profiles, adr_dict={1: 'foo', 2: 'bar'}) 14 | tree = stats.get_tree() 15 | assert tree == Node(1, 'foo', 2, {2: Node(2, 'bar', 2)}) 16 | assert repr(tree) == '' 17 | 18 | profiles = [([1, 2], 1, 1), 19 | ([1, 3], 1, 1)] 20 | stats = Stats(profiles, adr_dict={1: 'foo', 2: 'bar', 3: 'baz'}) 21 | tree = stats.get_tree() 22 | assert tree == Node(1, 'foo', 2, { 23 | 2: Node(2, 'bar', 1), 24 | 3: Node(3, 'baz', 1)}) 25 | 26 | def test_tree_jit(): 27 | profiles = [([1], 1, 1), 28 | ([1, AssemblerCode(100), JittedCode(1)], 1, 1)] 29 | stats = Stats(profiles, adr_dict={1: 'foo'}) 30 | tree = stats.get_tree() 31 | assert tree == Node(1, 'foo', 2) 32 | assert tree.meta['jit'] == 1 33 | 34 | def test_read_simple(): 35 | pytest.skip("think later") 36 | lib_cache = get_or_write_libcache('simple_nested.pypy.prof') 37 | path = py.path.local(__file__).join('..', 'simple_nested.pypy.prof') 38 | stats = vmprof.read_profile(path, virtual_only=True, 39 | include_extra_info=True, lib_cache=lib_cache) 40 | tree = stats.get_tree() 41 | main_name = 'py::2:foo.py' 42 | foo_name = 'py:foo:6:foo.py' 43 | bar_name = 'py:bar:2:foo.py' 44 | assert tree['foo'].name == foo_name 45 | assert tree['foo'].meta['jit'] == 19 46 | assert tree['foo']['bar'].name == bar_name 47 | assert tree['foo']['bar'].meta['jit'] == 101 48 | assert tree['foo'].jitcodes == {140523638277712: 120} 49 | assert tree['foo']['bar'].jitcodes == {140523638275600: 27, 50 | 140523638276656: 3} 51 | assert not tree['foo']['bar'].children 52 | assert tree['foo']['bar'].meta['gc:minor'] == 2 53 | data = json.loads(tree.as_json()) 54 | main_addr = str(tree.addr) 55 | foo_addr = str(tree['foo'].addr) 56 | bar_addr = str(tree['foo']['bar'].addr) 57 | expected = [main_name, main_addr, 120, {}, [ 58 | [foo_name, foo_addr, 120, {'jit': 19, 'gc:minor': 2}, [ 59 | [bar_name, bar_addr, 101, {'gc:minor': 2, 'jit': 101}, []]]]]] 60 | assert data == expected 61 | -------------------------------------------------------------------------------- /vmprof/test/wsgi.prof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmprof/vmprof-python/d30caa8f99535d1f5a27e27521f88d5d14d0e1e0/vmprof/test/wsgi.prof -------------------------------------------------------------------------------- /vmprof/upload.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import argparse 3 | from vmshare.service import Service 4 | 5 | def main(): 6 | parser = argparse.ArgumentParser() 7 | parser.add_argument("profile") 8 | parser.add_argument("--web-url", default='http://vmprof.com', help='Target host') 9 | parser.add_argument("--web-auth", default=None, help='Authtoken for your acount on the server') 10 | args = parser.parse_args() 11 | 12 | host, auth = args.web_url, args.web_auth 13 | filename = args.profile 14 | service = Service(host, auth) 15 | interpname = platform.python_implementation() 16 | service.post({ Service.FILE_CPU_PROFILE: filename, 17 | Service.FILE_JIT_PROFILE: filename + '.jit', 18 | 'argv': interpname + ' -m vmprof.upload ' + filename, 19 | 'VM': interpname }) 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /vmprofdemo.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | 4 | 5 | class Node(object): 6 | def __init__(self, right, left): 7 | self.left = left 8 | self.right = right 9 | 10 | 11 | class Digit(object): 12 | def __init__(self, v): 13 | self.v = v 14 | 15 | def eval(self): 16 | return ord(self.v) - ord('0') 17 | 18 | 19 | class Plus(Node): 20 | def eval(self): 21 | return self.left.eval() + self.right.eval() 22 | 23 | 24 | class Minus(Node): 25 | def eval(self): 26 | return self.left.eval() - self.right.eval() 27 | 28 | 29 | def parse_pn(text): 30 | digits = [chr(ord('0') + i) for i in range(10)] 31 | 32 | stack = [] 33 | for c in text: 34 | if c == '+': 35 | stack.append(Plus(stack.pop(), stack.pop())) 36 | elif c == '-': 37 | stack.append(Minus(stack.pop(), stack.pop())) 38 | elif c in digits: 39 | stack.append(Digit(c)) 40 | else: 41 | assert False 42 | assert len(stack) == 1 43 | return stack[0] 44 | 45 | 46 | def oracle(expr): 47 | return parse_pn(expr).eval() > 0 48 | 49 | 50 | def find(expr): 51 | cur_expr = expr 52 | if oracle(expr): 53 | cur = 0 54 | while oracle(cur_expr): 55 | cur += 1 56 | cur_expr = cur_expr + "1-" 57 | else: 58 | cur = 0 59 | while not oracle(cur_expr): 60 | cur += 1 61 | cur_expr = cur_expr + "1+" 62 | cur = -cur + 1 63 | return cur 64 | 65 | 66 | def gen_exp(lgt): 67 | stack_depth = 0 68 | exp = '' 69 | DIGITS = [chr(ord('0') + i) for i in range(10)] 70 | for i in range(lgt): 71 | if stack_depth > 1: 72 | c = random.choice(DIGITS + ['-', '+'] * 4) 73 | else: 74 | c = random.choice(DIGITS) 75 | if c in DIGITS: 76 | stack_depth += 1 77 | else: 78 | stack_depth -= 1 79 | exp += c 80 | while stack_depth > 1: 81 | exp += random.choice(['-', '+']) 82 | stack_depth -= 1 83 | return exp 84 | 85 | 86 | def fuzzer(count): 87 | for i in range(count): 88 | exp = gen_exp(10) 89 | assert parse_pn(exp).eval() == find(exp) 90 | 91 | 92 | if __name__ == '__main__': 93 | if len(sys.argv) == 2 and sys.argv[1] == 'demo': 94 | import time 95 | random.seed(42) 96 | l = [] 97 | for k in range(100): 98 | t0 = time.time() 99 | fuzzer(100) 100 | l.append(time.time() - t0) 101 | print("%.1f ms" % ((time.time() - t0) * 1000)) 102 | print("min: %.1fms max: %.1fms avg: %.1fms %.1fms" % (min(l) * 1000, max(l) * 1000, 103 | sum(l) / len(l) * 1000, sum(l[30:]) / (len(l) - 30) * 1000)) 104 | sys.exit(0) 105 | if len(sys.argv) > 1: 106 | count = int(sys.argv[1]) 107 | else: 108 | count = 10000 109 | if len(sys.argv) > 2: 110 | random.seed(int(sys.argv[2])) 111 | else: 112 | random.seed(13) 113 | fuzzer(count) 114 | -------------------------------------------------------------------------------- /vmshare/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /vmshare/binary.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import array 4 | import pytz 5 | 6 | WORD_SIZE = struct.calcsize('L') 7 | if sys.maxsize == 2**63-1: 8 | ADDR_SIZE = 8 9 | ADDR_CHAR = 'q' 10 | else: 11 | ADDR_SIZE = 4 12 | ADDR_CHAR = 'l' 13 | 14 | PY3 = sys.version_info[0] >= 3 15 | 16 | def read_word(fileobj): 17 | """Read a single long from `fileobj`.""" 18 | b = fileobj.read(WORD_SIZE) 19 | #do not use UNPACK_CHAR here 20 | r = int(struct.unpack('l', b)[0]) 21 | return r 22 | 23 | def read_addr(fileobj): 24 | return struct.unpack(ADDR_CHAR, fileobj.read(ADDR_SIZE))[0] 25 | 26 | def read_addresses(fileobj, count): 27 | """Read `addresses` longs from `fileobj`.""" 28 | if PY3: 29 | r = array.array(ADDR_CHAR) 30 | b = fileobj.read(ADDR_SIZE * count) 31 | r.frombytes(b) 32 | else: 33 | r = [struct.unpack(ADDR_CHAR, fileobj.read(ADDR_SIZE))[0] \ 34 | for i in range(count)] 35 | return r 36 | 37 | def read_byte(fileobj): 38 | value = fileobj.read(1) 39 | if PY3: 40 | return value[0] 41 | return ord(value[0]) 42 | 43 | def read_char(fileobj): 44 | value = fileobj.read(1) 45 | if PY3: 46 | return chr(value[0]) 47 | return value[0] 48 | 49 | def read_bytes(fileobj): 50 | lgt = int(struct.unpack('= 3 16 | 17 | class ServiceException(Exception): 18 | pass 19 | 20 | def is_errornous(json): 21 | return 'detail' in json 22 | 23 | def service_exception_from_json(json): 24 | assert 'error' in json 25 | return ServiceException(json['error']) 26 | 27 | 28 | def compress_file(filename): 29 | fileno, name = tempfile.mkstemp(suffix='.zip') 30 | os.close(fileno) 31 | with open(filename, 'rb') as fd: 32 | with gzip.open(name, 'wb') as zipfd: 33 | while True: 34 | chunk = fd.read(1024) 35 | if not chunk: 36 | break 37 | zipfd.write(chunk) 38 | return name 39 | 40 | class Service(object): 41 | FILE_CPU_PROFILE = 'cpu' 42 | FILE_MEM_PROFILE = 'mem' 43 | FILE_JIT_PROFILE = 'jit' 44 | 45 | def __init__(self, host, auth=None): 46 | self.host = host 47 | self.auth = auth 48 | self.runtime_id = None 49 | self.csrf_token = None 50 | 51 | def get_headers(self): 52 | base_headers = { 'Content-Type': 'application/json' } 53 | if self.auth: 54 | base_headers['AUTHORIZATION'] = "Token %s" % self.auth 55 | if self.csrf_token: 56 | base_headers['csrftoken'] = self.csrf_token 57 | return base_headers 58 | 59 | def get_url(self, path): 60 | host = self.host 61 | path = path.lstrip('/') 62 | if host.startswith("http"): 63 | url = '%s/%s' % (host.rstrip("/"), path) 64 | else: 65 | url = 'http://%s/%s' % (host.rstrip("/"), path) 66 | return url 67 | 68 | def stop_if_error_occured(self, response): 69 | if response.status_code != 200: 70 | sys.stderr.write("server rejected meta data." \ 71 | " status: %d, msg: '%s'\n" % \ 72 | (response.status_code, response.text)) 73 | raise ServiceException() 74 | 75 | def post_new_entry(self, data={}): 76 | url = self.get_url('/api/runtime/new') 77 | headers = self.get_headers() 78 | bytesdata = json.dumps(data).encode('utf-8') 79 | response = requests.post(url, data=bytesdata, headers=headers) 80 | self.stop_if_error_occured(response) 81 | j = response.json() 82 | return j.get('runtime_id', None) 83 | 84 | def post_file(self, rid, filename, filetype, compress=False): 85 | if not os.path.exists(filename): 86 | return None 87 | 88 | if compress: 89 | filename = compress_file(filename) 90 | 91 | if 'vmprof.com' in self.host: 92 | # not sure about nginx, what does MB mean? 1MB == 1000 byte or 1024 byte? 93 | # take the lower bound... 94 | size = os.path.getsize(filename) 95 | if size >= 100*1000*1000: # 100 MB 96 | warnings.warn("WARNING: vmprof.com limits the " 97 | "compressed profile file to 100 MBs. " 98 | "The upload might fail!\n") 99 | with open(filename, 'rb') as fd: 100 | url = self.get_url('/api/runtime/upload/%s/%s/add' % (filetype, rid)) 101 | headers = self.get_headers() 102 | headers['Content-Disposition'] = 'attachment; filename='+filename 103 | del headers['Content-Type'] 104 | response = requests.post(url, headers=headers, data=fd) 105 | self.stop_if_error_occured(response) 106 | return response 107 | 108 | def post(self, kwargs): 109 | sys.stderr.write("Uploading to %s...\n" % self.host) 110 | 111 | argv = kwargs.get('argv', '-') 112 | vm = kwargs.get('VM','unknown') 113 | 114 | rid = self.post_new_entry({'argv': argv, 'VM': vm}) 115 | if rid is None: 116 | raise ServiceException("could not create meta data for profiles") 117 | 118 | if Service.FILE_CPU_PROFILE in kwargs: 119 | filename = kwargs[Service.FILE_CPU_PROFILE] 120 | if os.path.exists(filename): 121 | sys.stderr.write(" => Uploading the cpu profile...\n") 122 | self.post_file(rid, filename, 123 | Service.FILE_CPU_PROFILE, compress=False) 124 | sys.stderr.write(' ' + self.get_url('#/%s' % rid) + "\n") 125 | if Service.FILE_JIT_PROFILE in kwargs: 126 | filename = kwargs[Service.FILE_JIT_PROFILE] 127 | if os.path.exists(filename): 128 | sys.stderr.write(" => Uploading the jit log...\n") 129 | forest = parse_jitlog(filename) 130 | if forest.exception_raised(): 131 | sys.stderr.write(" error: %s\n" % forest.exception_raised()) 132 | # append source code to the binary 133 | forest.extract_source_code_lines() 134 | forest.copy_and_add_source_code_tags() 135 | filename = forest.filepath 136 | response = self.post_file(rid, filename, 137 | Service.FILE_JIT_PROFILE, compress=True) 138 | forest.unlink_jitlog() 139 | json = response.json() 140 | if 'jid' in json: 141 | url = self.get_url('#/%s/traces' % json['jid']) 142 | else: 143 | url = self.get_url('#/%s' % rid) 144 | sys.stderr.write(' ' + url + "\n") 145 | 146 | self.finalize_entry(rid) 147 | 148 | 149 | def finalize_entry(self, rid, data=b""): 150 | url = self.get_url('/api/runtime/%s/freeze/' % rid) 151 | headers = self.get_headers() 152 | response = requests.post(url, data=data, headers=headers) 153 | self.crsf_token = None 154 | if response.status_code != 200: 155 | sys.stderr.write("server failed to freeze these runtime profiles." \ 156 | " status: %d, msg: '%s'\n" % \ 157 | (response.status_code, response.text)) 158 | return False 159 | return True 160 | 161 | --------------------------------------------------------------------------------