Giter Site home page Giter Site logo

bloomberg / memray Goto Github PK

View Code? Open in Web Editor NEW
12.6K 62.0 361.0 27.78 MB

Memray is a memory profiler for Python

Home Page: https://bloomberg.github.io/memray/

License: Apache License 2.0

Dockerfile 0.06% Makefile 0.38% Python 72.72% Cython 4.43% C++ 18.45% JavaScript 2.45% CSS 0.19% HTML 0.94% C 0.24% CMake 0.09% GDB 0.04%
memory memory-leak memory-leak-detection memory-profiler profiler python python3 hacktoberfest

memray's Introduction


OS Linux OS MacOS PyPI - Python Version PyPI - Implementation PyPI PyPI - Downloads Conda Version Tests Code Style

Memray output

Memray is a memory profiler for Python. It can track memory allocations in Python code, in native extension modules, and in the Python interpreter itself. It can generate several different types of reports to help you analyze the captured memory usage data. While commonly used as a CLI tool, it can also be used as a library to perform more fine-grained profiling tasks.

Notable features:

  • ๐Ÿ•ต๏ธโ€โ™€๏ธ Traces every function call so it can accurately represent the call stack, unlike sampling profilers.
  • โ„ญ Also handles native calls in C/C++ libraries so the entire call stack is present in the results.
  • ๐ŸŽ Blazing fast! Profiling slows the application only slightly. Tracking native code is somewhat slower, but this can be enabled or disabled on demand.
  • ๐Ÿ“ˆ It can generate various reports about the collected memory usage data, like flame graphs.
  • ๐Ÿงต Works with Python threads.
  • ๐Ÿ‘ฝ๐Ÿงต Works with native-threads (e.g. C++ threads in C extensions).

Memray can help with the following problems:

  • Analyze allocations in applications to help discover the cause of high memory usage.
  • Find memory leaks.
  • Find hotspots in code that cause a lot of allocations.

Note Memray only works on Linux and MacOS, and cannot be installed on other platforms.

Help us improve Memray!

We are constantly looking for feedback from our awesome community โค๏ธ. If you have used Memray to solve a problem, profile an application, find a memory leak or anything else, please let us know! We would love to hear about your experience and how Memray helped you.

Please, consider writing your story in the Success Stories discussion page.

It really makes a difference!

Installation

Memray requires Python 3.7+ and can be easily installed using most common Python packaging tools. We recommend installing the latest stable release from PyPI with pip:

    python3 -m pip install memray

Notice that Memray contains a C extension so releases are distributed as binary wheels as well as the source code. If a binary wheel is not available for your system (Linux x86/x64 or macOS), you'll need to ensure that all the dependencies are satisfied on the system where you are doing the installation.

Building from source

If you wish to build Memray from source you need the following binary dependencies in your system:

  • libunwind (for Linux)
  • liblz4

Check your package manager on how to install these dependencies (for example apt-get install libunwind-dev liblz4-dev in Debian-based systems or brew install lz4 in MacOS). Note that you may need to teach the compiler where to find the header and library files of the dependencies. For example, in MacOS with brew you may need to run:

export CFLAGS="-I$(brew --prefix lz4)/include" LDFLAGS="-L$(brew --prefix lz4)/lib -Wl,-rpath,$(brew --prefix lz4)/lib"

before installing memray. Check the documentation of your package manager to know the location of the header and library files for more detailed information.

If you are building on MacOS, you will also need to set the deployment target.

export MACOSX_DEPLOYMENT_TARGET=10.14

Once you have the binary dependencies installed, you can clone the repository and follow with the normal building process:

git clone [email protected]:bloomberg/memray.git memray
cd memray
python3 -m venv ../memray-env/  # just an example, put this wherever you want
source ../memray-env/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -e . -r requirements-test.txt -r requirements-extra.txt

This will install Memray in the virtual environment in development mode (the -e of the last pip install command).

If you plan to contribute back, you should install the pre-commit hooks:

pre-commit install

This will ensure that your contribution passes our linting checks.

Documentation

You can find the latest documentation available here.

Usage

There are many ways to use Memray. The easiest way is to use it as a command line tool to run your script, application, or library.

usage: memray [-h] [-v] {run,flamegraph,table,live,tree,parse,summary,stats} ...

Memory profiler for Python applications

Run `memray run` to generate a memory profile report, then use a reporter command
such as `memray flamegraph` or `memray table` to convert the results into HTML.

Example:

    $ python3 -m memray run -o output.bin my_script.py
    $ python3 -m memray flamegraph output.bin

positional arguments:
  {run,flamegraph,table,live,tree,parse,summary,stats}
                        Mode of operation
    run                 Run the specified application and track memory usage
    flamegraph          Generate an HTML flame graph for peak memory usage
    table               Generate an HTML table with all records in the peak memory usage
    live                Remotely monitor allocations in a text-based interface
    tree                Generate a tree view in the terminal for peak memory usage
    parse               Debug a results file by parsing and printing each record in it
    summary             Generate a terminal-based summary report of the functions that allocate most memory
    stats               Generate high level stats of the memory usage in the terminal

optional arguments:
  -h, --help            Show this help message and exit
  -v, --verbose         Increase verbosity. Option is additive and can be specified up to 3 times
  -V, --version         Displays the current version of Memray

Please submit feedback, ideas, and bug reports by filing a new issue at https://github.com/bloomberg/memray/issues

To use Memray over a script or a single python file you can use:

python3 -m memray run my_script.py

If you normally run your application with python3 -m my_module, you can use the -m flag with memray run:

python3 -m memray run -m my_module

You can also invoke Memray as a command line tool without having to use -m to invoke it as a module:

memray run my_script.py
memray run -m my_module

The output will be a binary file (like memray-my_script.2369.bin) that you can analyze in different ways. One way is to use the memray flamegraph command to generate a flame graph:

memray flamegraph my_script.2369.bin

This will produce an HTML file with a flame graph of the memory usage that you can inspect with your favorite browser. There are multiple other reporters that you can use to generate other types of reports, some of them generating terminal-based output and some of them generating HTML files. Here is an example of a Memray flamegraph:

Pytest plugin

If you want an easy and convenient way to use memray in your test suite, you can consider using pytest-memray. Once installed, this pytest plugin allows you to simply add --memray to the command line invocation:

pytest --memray tests/

And will automatically get a report like this:

python3 -m pytest tests --memray
=============================================================================================================================== test session starts ================================================================================================================================
platform linux -- Python 3.8.10, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /mypackage, configfile: pytest.ini
plugins: cov-2.12.0, memray-0.1.0
collected 21 items

tests/test_package.py .....................                                                                                                                                                                                                                      [100%]


================================================================================================================================= MEMRAY REPORT ==================================================================================================================================
Allocations results for tests/test_package.py::some_test_that_allocates

	 ๐Ÿ“ฆ Total memory allocated: 24.4MiB
	 ๐Ÿ“ Total allocations: 33929
	 ๐Ÿ“Š Histogram of allocation sizes: |โ–‚   โ–ˆ    |
	 ๐Ÿฅ‡ Biggest allocating functions:
		- parse:/opt/bb/lib/python3.8/ast.py:47 -> 3.0MiB
		- parse:/opt/bb/lib/python3.8/ast.py:47 -> 2.3MiB
		- _visit:/opt/bb/lib/python3.8/site-packages/astroid/transforms.py:62 -> 576.0KiB
		- parse:/opt/bb/lib/python3.8/ast.py:47 -> 517.6KiB
		- __init__:/opt/bb/lib/python3.8/site-packages/astroid/node_classes.py:1353 -> 512.0KiB

You can also use some of the included markers to make tests fail if the execution of said test allocates more memory than allowed:

@pytest.mark.limit_memory("24 MB")
def test_foobar():
    # do some stuff that allocates memory

To learn more on how the plugin can be used and configured check out the plugin documentation.

Native mode

Memray supports tracking native C/C++ functions as well as Python functions. This can be especially useful when profiling applications that have C extensions (such as numpy or pandas) as this gives a holistic vision of how much memory is allocated by the extension and how much is allocated by Python itself.

To activate native tracking, you need to provide the --native argument when using the run subcommand:

memray run --native my_script.py

This will automatically add native information to the result file and it will be automatically used by any reporter (such the flamegraph or table reporters). This means that instead of seeing this in the flamegraphs:

You will now be able to see what's happening inside the Python calls:

Reporters display native frames in a different color than Python frames. They can also be distinguished by looking at the file location in a frame (Python frames will generally be generated from files with a .py extension while native frames will be generated from files with extensions like .c, .cpp or .h).

Live mode

Memray output

Memray's live mode runs a script or a module in a terminal-based interface that allows you to interactively inspect its memory usage while it runs. This is useful for debugging scripts or modules that take a long time to run or that exhibit multiple complex memory patterns. You can use the --live option to run the script or module in live mode:

    memray run --live my_script.py

or if you want to execute a module:

    memray run --live -m my_module

This will show the following TUI interface in your terminal:

Sorting results

The results are displayed in descending order of total memory allocated by a function and the subfunctions called by it. You can change the ordering with the following keyboard shortcuts:

  • t (default): Sort by total memory

  • o: Sort by own memory

  • a: Sort by allocation count

In most terminals you can also click the "Sort by Total", "Sort by Own", and "Sort by Allocations" buttons on the footer.

The sorted column's heading is underlined.

Viewing different threads

By default, the live command will present the main thread of the program. You can look at different threads of the program by pressing the greater than and less than keys, < and >. In most terminals you can also click the "Previous Thread" and "Next Thread" buttons on the footer.

API

In addition to tracking Python processes from a CLI using memray run, it is also possible to programmatically enable tracking within a running Python program.

import memray

with memray.Tracker("output_file.bin"):
    print("Allocations will be tracked until the with block ends")

For details, see the API documentation.

License

Memray is Apache-2.0 licensed, as found in the LICENSE file.

Code of Conduct

This project has adopted a Code of Conduct. If you have any concerns about the Code, or behavior that you have experienced in the project, please contact us at [email protected].

Security Policy

If you believe you have identified a security vulnerability in this project, please send an email to the project team at [email protected], detailing the suspected issue and any methods you've found to reproduce it.

Please do NOT open an issue in the GitHub repository, as we'd prefer to keep vulnerability reports private until we've had an opportunity to review and address them.

Contributing

We welcome your contributions to help us improve and extend this project!

Below you will find some basic steps required to be able to contribute to the project. If you have any questions about this process or any other aspect of contributing to a Bloomberg open source project, feel free to send an email to [email protected] and we'll get your questions answered as quickly as we can.

Contribution Licensing

Since this project is distributed under the terms of an open source license, contributions that you make are licensed under the same terms. In order for us to be able to accept your contributions, we will need explicit confirmation from you that you are able and willing to provide them under these terms, and the mechanism we use to do this is called a Developer's Certificate of Origin (DCO). This is very similar to the process used by the Linux kernel, Samba, and many other major open source projects.

To participate under these terms, all that you must do is include a line like the following as the last line of the commit message for each commit in your contribution:

Signed-Off-By: Random J. Developer <[email protected]>

The simplest way to accomplish this is to add -s or --signoff to your git commit command.

You must use your real name (sorry, no pseudonyms, and no anonymous contributions).

Steps

  • Create an Issue, select 'Feature Request', and explain the proposed change.
  • Follow the guidelines in the issue template presented to you.
  • Submit the Issue.
  • Submit a Pull Request and link it to the Issue by including "#" in the Pull Request summary.

memray's People

Contributors

adrinjalali avatar aelsayed95 avatar apurvakhatri avatar ayshiff avatar bilbofroggins avatar cclauss avatar davide125 avatar dependabot[bot] avatar drz416 avatar elfosardo avatar godlygeek avatar greydoubt avatar gusmonod avatar jcarnaxide avatar kakun45 avatar lkollar avatar matthiasdiener avatar mgmacias95 avatar ochaloup avatar pablogsal avatar pradyunsg avatar salticus avatar sjirwin avatar stefmolin avatar subbrammanian avatar tal66 avatar tiangolo avatar tjmcewan avatar wilddandan avatar xlanor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

memray's Issues

Slow run and error generating flamegraph

Bug Report

Current Behavior Running memray on a HPC cluster took several hours, when the same input runs in minutes on my laptop. After the run finally finished, I got the following error trying to generate the flamegraph:

Traceback (most recent call last):
  File "/cluster/software/Python/3.9.6-GCCcore-11.2.0/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/cluster/software/Python/3.9.6-GCCcore-11.2.0/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/__main__.py", line 6, in <module>
    sys.exit(main())
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/commands/__init__.py", line 124, in main
    arg_values.entrypoint(arg_values, parser)
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/commands/common.py", line 117, in run
    self.write_report(result_path, output_file, args.show_memory_leaks, **kwargs)
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/commands/common.py", line 86, in write_report
    reporter = self.reporter_factory(
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/reporters/flamegraph.py", line 116, in from_snapshot
    transformed_data = with_converted_children_dict(data)
  File "/cluster/projects/nn4654k/hmcezar/venv/hymd/lib/python3.9/site-packages/memray/reporters/flamegraph.py", line 31, in with_converted_children_dict
    the_node["children"] = [child for child in the_node["children"].values()]
AttributeError: 'list' object has no attribute 'values'

Input Code

python -m memray run --native --follow-fork --trace-python-allocators -m hymd dpc.toml final_step1_centered.hdf5 --seed 400755
python -m memray flamegraph memray-hymd.78911.bin

Expected behavior/code I expected to run memray in about the same time it takes on my laptop, and in the end get a flamegraph.

Environment

  • Python(s): Python 3.9
  • Modules
Currently Loaded Modules:
  1) StdEnv                        (S)   7) XZ/5.2.5-GCCcore-11.2.0          (H)  13) gompi/2021b                         19) Tcl/8.6.11-GCCcore-11.2.0   (H)
  2) GCCcore/11.2.0                      8) libxml2/2.9.10-GCCcore-11.2.0    (H)  14) Szip/2.1.1-GCCcore-11.2.0      (H)  20) SQLite/3.36-GCCcore-11.2.0  (H)
  3) zlib/1.2.11-GCCcore-11.2.0    (H)   9) libpciaccess/0.16-GCCcore-11.2.0 (H)  15) HDF5/1.13.1-gompi-2021b             21) GMP/6.2.1-GCCcore-11.2.0    (H)
  4) binutils/2.37-GCCcore-11.2.0  (H)  10) hwloc/2.5.0-GCCcore-11.2.0       (H)  16) bzip2/1.0.8-GCCcore-11.2.0     (H)  22) libffi/3.4.2-GCCcore-11.2.0 (H)
  5) GCC/11.2.0                         11) hpcx/2.9                              17) ncurses/6.2-GCCcore-11.2.0     (H)  23) OpenSSL/1.1                 (H)
  6) numactl/2.0.14-GCCcore-11.2.0 (H)  12) OpenMPI/4.1.1-GCC-11.2.0              18) libreadline/8.1-GCCcore-11.2.0 (H)  24) Python/3.9.6-GCCcore-11.2.0
  • PyPI packages
attrs==21.4.0
commonmark==0.9.1
Cython==0.29.30
guppy3==3.1.2
h5py==3.7.0
-e git+https://github.com/hmcezar/HyMD.git@5fbd0a56c3259fa3838e449b11401b42bacc1694#egg=hymd
iniconfig==1.1.1
Jinja2==3.1.2
MarkupSafe==2.1.1
memory-profiler==0.60.0
memray==1.2.0
mpi4py==3.1.3
mpmath==1.2.1
mpsort==0.1.17
networkx==2.8.4
numpy==1.23.0
packaging==21.3
pfft-python==0.1.21
pluggy==1.0.0
plumed==2.9.0.dev0
pmesh==0.1.56
psutil==5.9.1
py==1.11.0
Pygments==2.12.0
pyparsing==3.0.9
pytest==7.1.2
pytest-mpi==0.6
rich==12.5.1
sympy==1.10.1
tomli==2.0.1

Extra information memray table runs, giving the following output:
memray-table-hymd.78911.zip

ZeroDivisionError on TUI

Bug Report

Current Behavior TUI crashes with a ZeroDivisionError when pressing "right arrow" after program has completed.

Input Code

Run memray run --live -c 'print(1)'. It completes shortly, and the TUI opens saying "Thread 1 of 0". Pressing the right arrow key to cycle between threads crashes with:

Traceback (most recent call last):
  File "/.../venv/bin/memray", line 8, in <module>
    sys.exit(main())
  File "/.../venv/lib/python3.9/site-packages/memray/commands/__init__.py", line 124, in main
    arg_values.entrypoint(arg_values, parser)
  File "/.../venv/lib/python3.9/site-packages/memray/commands/run.py", line 308, in run
    _run_child_process_and_attach(args)
  File "/.../venv/lib/python3.9/site-packages/memray/commands/run.py", line 117, in _run_child_process_and_attach
    raise error from None
  File "/.../venv/lib/python3.9/site-packages/memray/commands/run.py", line 114, in _run_child_process_and_attach
    LiveCommand().start_live_interface(port)
  File "/.../venv/lib/python3.9/site-packages/memray/commands/live.py", line 110, in start_live_interface
    tui.previous_thread()
  File "/.../venv/lib/python3.9/site-packages/memray/reporters/tui.py", line 371, in previous_thread
    self._thread_idx = (self._thread_idx - 1) % len(self._threads)
ZeroDivisionError: integer division or modulo by zero

Expected behavior/code no crash

Environment

  • Python(s): 3.9

Possible Solution

n/a

Additional context/Screenshots n/a

Using more cpu cores to processing .bin files

Feature Request

Is your feature request related to a problem? Please describe.
I have several 5GB .bin files, when I run 'memray tree 5GB-bin-file', it takes ten more minutes to get results and memray uses 100% cpu. So is there a method to process one .bin file parallel using more cpu cores ?

Describe the solution you'd like
Use more cpu cores to process .bin files.

Segfault with Sqlalchemy

Bug Report

Memray triggers segfault running Sqlalchemy code using greenlets.

Program terminated with signal SIGSEGV, Segmentation fault.
#0  PyCode_Addr2Line (addrq=0, co=0x66) at Objects/codeobject.c:1248
1248        if (addrq < 0) {
[Current thread is 1 (Thread 0x7fef9e344200 (LWP 23383))]
#0  PyCode_Addr2Line (addrq=0, co=0x66) at Objects/codeobject.c:1248
#1  PyFrame_GetLineNumber (f=<unknown at remote 0x5594cd476a00>) at Objects/frameobject.c:49
#2  0x00007fef9dc2a287 in memray::tracking_api::PythonStackTracker::pushPythonFrame(_frame*) [clone .constprop.52] ()
  from /home/petros/.local/lib/python3.10/site-packages/memray/_memray.cpython-310-x86_64-linux-gnu.so
#3  0x00007fef9dc2b5b8 in memray::tracking_api::PyTraceFunction(_object*, _frame*, int, _object*) ()
  from /home/petros/.local/lib/python3.10/site-packages/memray/_memray.cpython-310-x86_64-linux-gnu.so
#4  0x00007fef9e870e55 in call_trace (func=0x7fef9dc2b4c0 <memray::tracking_api::PyTraceFunction(_object*, _frame*, int,  _object*)>, obj=123, tstate=0x5594cc6614e0, 
   frame=Frame 0x7fef98c76640, for file /home/petros/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py, line 1330, in execute (self=<Connection(engine=<Engine(pool=<AsyncAdaptedQueuePool(_orig_logging_name=None, _echo=None,  logger=<Logger(filters=[], name='sqlalchemy.pool.impl.AsyncAdaptedQueuePool', level=0, parent=<Logger(filters=[],  name='sqlalchemy', level=30, parent=<RootLogger(filters=[], name='root', level=30, parent=None, propagate=True, handlers=[], disabled=False, _cache={}) at remote 0x7fef9d1b3880>, propagate=True, handlers=[], disabled=False, _cache={}, manager=?<Manager(root=<...>, _disable=0, emittedNoHandlerWarning=False, loggerDict={'rich': <Logger(filters=[], name='rich', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9d0dffd0>, 'concurrent.futures': <Logger(filters=[], name='concurrent.futures', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9c8ab820>, 'concurrent'...(truncated), trace_info=<optimized out>, what=0, arg=None) at Python/ceval.c:5468
#5  0x00007fef9e870f40 in call_trace_protected (func=<optimized out>, obj=<optimized out>, tstate=0x5594cc6614e0, frame=<optimized out>, trace_info=<optimized out>, what=<optimized out>, 
    arg=None) at Python/ceval.c:5427
#6  0x00007fef9e6c737d in _PyEval_EvalFrameDefault (tstate=0x5594cc6614e0, 
    f=Frame 0x7fef98c76640, for file /home/petros/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py, line 1330, in execute (self=<Connection(engine=<Engine(pool=<AsyncAdaptedQueuePool(_orig_logging_name=None, _echo=None, logger=<Logger(filters=[], name='sqlalchemy.pool.impl.AsyncAdaptedQueuePool', level=0, parent=<Logger(filters=[], name='sqlalchemy', level=30, parent=<RootLogger(filters=[], name='root', level=30, parent=None, propagate=True, handlers=[], disabled=False, _cache={}) at remote 0x7fef9d1b3880>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<Manager(root=<...>, _disable=0, emittedNoHandlerWarning=False, loggerDict={'rich': <Logger(filters=[], name='rich', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9d0dffd0>, 'concurrent.futures': <Logger(filters=[], name='concurrent.futures', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9c8ab820>, 'concurrent'...(truncated), throwflag=0) at Python/ceval.c:1654
#7  0x00007fef9e77be93 in _PyEval_EvalFrame (throwflag=0, 
    f=Frame 0x7fef98c76640, for file /home/petros/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py, line 1330, in execute (self=<Connection(engine=<Engine(pool=<AsyncAdaptedQueuePool(_orig_logging_name=None, _echo=None, logger=<Logger(filters=[], name='sqlalchemy.pool.impl.AsyncAdaptedQueuePool', level=0, parent=<Logger(filters=[], name='sqlalchemy', level=30, parent=<RootLogger(filters=[], name='root', level=30, parent=None, propagate=True, handlers=[], disabled=False, _cache={}) at remote 0x7fef9d1b3880>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<Manager(root=<...>, _disable=0, emittedNoHandlerWarning=False, loggerDict={'rich': <Logger(filters=[], name='rich', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9d0dffd0>, 'concurrent.futures': <Logger(filters=[], name='concurrent.futures', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9c8ab820>, 'concurrent'...(truncated), tstate=0x5594cc6614e0) at ./Include/internal/pycore_ceval.h:46
#8  _PyEval_Vector (tstate=<optimized out>, con=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=3, kwnames=<optimized out>) at Python/ceval.c:5065
#9  0x00007fef9e78e984 in _PyFunction_Vectorcall (kwnames=<optimized out>, nargsf=3, stack=0x5594cdf53fe8, func=<function at remote 0x7fef9a7af490>) at Objects/call.c:342
#10 _PyObject_VectorcallTstate (kwnames=<optimized out>, nargsf=3, args=0x5594cdf53fe8, callable=<function at remote 0x7fef9a7af490>, tstate=0x5594cc6614e0) at ./Include/cpython/abstract.h:114
#11 method_vectorcall (method=<optimized out>, args=0x5594cdf53ff0, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/classobject.c:53
#12 0x00007fef9e7f0fe2 in _PyObject_VectorcallTstate (tstate=0x5594cc6614e0, callable=<method at remote 0x7fef98b44fc0>, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>)
    at ./Include/cpython/abstract.h:114
#13 0x00007fef9e783f8b in call_function (tstate=<optimized out>, trace_info=<optimized out>, pp_stack=0x7ffebfe6ccb0, oparg=<optimized out>, kwnames=<optimized out>) at Python/ceval.c:5866
#14 0x00007fef9e77dfd5 in _PyEval_EvalFrameDefault (tstate=<optimized out>, f=<optimized out>, throwflag=<optimized out>) at Python/ceval.c:4231
#15 0x00007fef9e77be93 in _PyEval_EvalFrame (throwflag=0, 
    f=Frame 0x5594cdf53e00, for file /home/petros/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py, line 1847, in _execute_internal (self=<Session(identity_map=<WeakInstanceDict(_dict={}, _modified=set(), _wr=<weakref at remote 0x7fef98b22b60>) at remote 0x7fef98b2b940>, _new={}, _deleted={}, bind=<Engine(pool=<AsyncAdaptedQueuePool(_orig_logging_name=None, _echo=None, logger=<Logger(filters=[], name='sqlalchemy.pool.impl.AsyncAdaptedQueuePool', level=0, parent=<Logger(filters=[], name='sqlalchemy', level=30, parent=<RootLogger(filters=[], name='root', level=30, parent=None, propagate=True, handlers=[], disabled=False, _cache={}) at remote 0x7fef9d1b3880>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<Manager(root=<...>, _disable=0, emittedNoHandlerWarning=False, loggerDict={'rich': <Logger(filters=[], name='rich', level=0, parent=<...>, propagate=True, handlers=[], disabled=False, _cache={}, manager=<...>) at remote 0x7fef9d0dffd0>, 'concurrent.futures': <Logger(filters=[], name='co...(truncated), tstate=0x5594cc6614e0) at ./Include/internal/pycore_ceval.h:46
#16 _PyEval_Vector (tstate=<optimized out>, con=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=3, kwnames=<optimized out>) at Python/ceval.c:5065
#17 0x00007fef9e78e984 in _PyFunction_Vectorcall (kwnames=<optimized out>, nargsf=3, stack=0x7fef98b39728, func=<function at remote 0x7fef9a4b0820>) at Objects/call.c:342
#18 _PyObject_VectorcallTstate (kwnames=<optimized out>, nargsf=3, args=0x7fef98b39728, callable=<function at remote 0x7fef9a4b0820>, tstate=0x5594cc6614e0) at ./Include/cpython/abstract.h:114

Input Code

import asyncio
from sqlalchemy import Column, Integer, MetaData, String
from sqlalchemy.future import select
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy.schema import Table
from sqlalchemy.ext.asyncio import async_scoped_session, AsyncSession, create_async_engine

metadata = MetaData()
Base = declarative_base(metadata=metadata)

t1 = Table(
    "t1",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("d1", String(50)),
    Column("d2", String(50)),
    Column("d3", String(50)),
    Column("d4", String(50)),
    Column("d5", String(50)),
)


engine = engine = create_async_engine(
    "postgresql+psycopg://@/",
    future=True,
    hide_parameters=False,
    max_overflow=50,
    pool_size=30,
)

session_factory = sessionmaker(
    engine,
    expire_on_commit=True,
    autoflush=False,
    class_=AsyncSession,
    future=True,
)

db_session = async_scoped_session(session_factory, scopefunc=asyncio.current_task)


async def stream():
    async with db_session() as session:
        async for partition in (await session.stream(select(t1.c.id))).scalars().partitions(int(1e3)):
            yield partition
            await asyncio.sleep(0)

async def retrieve(id_):
    async with db_session() as session:
        return (await session.execute(select(t1).where(t1.c.id == id_))).scalar_one()


async def test():
    async for partition in stream():
        r = await asyncio.gather(*(retrieve(id_) for id_ in partition))
        print(len(r))


asyncio.run(test())

It seems that the pointer argument to PyCode_Addr2Line is invalid. Testing with PYTHONDEVMODE=1, it becomes 0xdddddddddddddddd, which corresponds to newly freed memory.

Expected behavior/code
No crash.

Environment

  • Python(s): Python 3.10.5
  • sqlalchemy master
  • psycopg 3.0.15
  • openSUSE Tumbleweed

Support for Pypy

Thanks for releasing Memray, I like the elegance of it's api. I'm curious if it'll be possible to use Memray with Pypy? Currently, I can't build from source in a Pypy 3.8 virtual env. I have ibunwind-dev and liblz4-dev installed, and I can build from the source inside CPython venv.

Is adding support for Pypy a stretch or there's something I can do to successfully build in Pypy?

Display the stack that generated the memory peak

Feature Request

When looking at a flamegraph and trying to understand when it happens in my application realized I don't really know where the application is in its stack trace when it hits the peek memory usage. It would be great if this could be shown (either part of the HTML or generated separately).

The `memray stats` output is misleading

Bug Report

Current Behavior

The stats reporter shows a histogram labelled as:

๐Ÿ“ Total allocations:
        2468520

๐Ÿ“ฆ Total memory allocated:
        257.024MB

  Histogram of allocation size:
        min: 1.000B
        ---------------------------------------------
        < 4.000B   :    122 โ–‡
        < 24.000B  :  57425 โ–‡โ–‡โ–‡โ–‡โ–‡
        < 119.000B : 302549 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 588.000B :  93915 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 2.829KB  :  26592 โ–‡โ–‡โ–‡
        < 13.933KB :   7789 โ–‡
        < 68.616KB :   1734 โ–‡
        < 337.926KB:    222 โ–‡
        < 1.625MB  :     13 โ–‡
        <=8.004MB  :     14 โ–‡
        ---------------------------------------------
        max: 8.004MB

But the histogram isn't really "of allocation size", because the stats reporter is only seeing allocations after they've already been aggregated by the SnapshotAllocationAggregator. The stats reporter has absolutely no idea how big or small any single allocation was, because it only sees the sum of the sizes of the allocations performed by any given source location.

If we wanted this histogram to be correct, we'd need more information to survive the aggregation process: namely, the size of each individual allocation that contributed to the aggregated size associated with a source location.

Moreover, it's at least somewhat misleading that this histogram shows (by default) only allocations that haven't been freed. Nothing in the displayed stats indicates that the histogram is showing only allocated-but-not-freed things as of the high water mark, and in fact the labels above it ("Total allocations" and "Total memory allocated") make it sound like it is considering all allocations, not just unfreed allocations as of the high water mark (I'd expect "Maximum memory allocated" rather than "Total memory allocated" to describe the latter case).

Adding support for Windows

Feature Request

I tried pip installing Memray on my windows PC but I failed. The error showed that "Memray only supports Linux platforms."
By any chance is there a way of adding support for windows? I'm happy to work on a windows port if it's in the cards (and if I get the hang of the project internals)

Information about which allocator was used is lost in roll-up

Consider this code:

import mmap

for i in range(5):
    x = mmap.mmap(-1, 10000000000) if i == 0 else "x" * 1000000

This is what we currently show in the stats reporter:

๐Ÿ“ Total allocations:
        21

๐Ÿ“ฆ Total memory allocated:
        9.314GB

๐Ÿ“Š Histogram of allocation size:
        min: 528.000B
        ----------------------------------------
        < 2.754KB  : 8 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 14.717KB : 2 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 78.624KB : 0
        < 420.047KB: 0
        < 2.191MB  : 0
        < 11.708MB : 0
        < 62.549MB : 0
        < 334.166MB: 0
        < 1.743GB  : 0
        <=9.314GB  : 1 โ–‡โ–‡โ–‡โ–‡
        ----------------------------------------
        max: 9.314GB

๐Ÿ“‚ Allocator type distribution:
         MALLOC: 11

The allocator type distribution is incorrect - it says there were no MMAP allocations and only MALLOC allocations. The fact that different types of allocations occurred on the same line is being lost.

gcc/4.7.2/lib64/libstdc++.so.6: version `GLIBCXX_3.4.18'

Bug Report

Current Behavior A clear and concise description of the behavior.
When running my package, which uses a pre-built c packages as-well, it fails on GLIBC stuff.
When I run my package standalone without memray, it does work.

ImportError: /***/pkgs/gcc/4.7.2/lib64/libstdc++.so.6: version `GLIBCXX_3.4.18' not found (required by ***.cpython-37m-x86_64-linux-gnu.so)

Input Code

  • REPL or Repo link if applicable:
    Not applicable

Expected behavior/code A clear and concise description of what you expected to happen (or
code).

It should run the same as running my main module

Environment

  • Python(s): Python 3.7.4

Possible Solution

Additional context/Screenshots Add any other context about the problem here. If applicable, add
screenshots to help explain.

Crashes while processing large output binary

We have an application which uses a lot of memory > 30GB and we're trying to understand where the memory is used. The output binary file is 150GB+ and when try to create a table /HTML / summary it crashes with out of memory errors because it seems to try to process the entire large file in memory at once.

Is multi-processing supported?

Thank you guys for this amazing beautiful cool tool!

Feature Request

I am dealing with some memory problems related to pytorch dataloader for several days. And just tried memray with a simple script below. I found that in the live mode, the information of main process is reported but all processes are detected as threads and no information is reported.

from torch.utils.data import Dataset, DataLoader
import numpy as np
import torch
import sys

class DataIter(Dataset):
    def __init__(self):
        n = int(2.4e7)
        self.data = [x for x in range(n)]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        data = np.array([data], dtype=np.int64)
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=12)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i, end='\t', flush=True)

Screenshot of main process:
Screenshot from 2022-04-21 22-56-22

screen shot of other process:

Screenshot from 2022-04-21 22-50-01

The following command memray run --live simple_multi_worker.py is used.

Is there a way to observe multi-processing information?

Add manpages for the memray cli

Feature Request

Is your feature request related to a problem? Please describe.
N/A.

Describe the solution you'd like
Provide manpages for the memray tool

Describe alternatives you've considered
Could be as simple for start as the help page

Teachability, Documentation, Adoption, Migration Strategy
Users would RTFM :)

Publish wheel for aarch64

I went to install memray on my aarch64 box and had to install a load of packages to build it myself. Please consider also building aarch64 wheels.

Incorrect quoting of popen arguments in `memray run --live`

Bug Report

This command works as expected:

memray run --live -c "print('hi')"

This command hangs forever:

memray run --live -c 'print("hi")'

--live mode tries to fork off a subprocess and run the command in that subprocess, and the way the arguments are being marshalled from the parent process to the child process fails if double quotes are used in the command or script name.

Plus, the parent process hangs forever if the child process fails to start up, so that's a separate issue.

Consider signing tags and commits

Feature Request

Is your feature request related to a problem? Please describe.
As the package maintainer of this in Arch Linux I would appreciate to maintaining the chain of trust with PGP signatures on commits / tags. This can be handled from the Arch Linux package manager natively and can validate the tag / commit against the PGP public key of the author of the commit.

Describe the solution you'd like

  • Sign both commits and tags at least during releases but preferably all.
  • Mention the public keys used for signing those releases in README so downstream systems can validate independently.
  • Add any new maintainers who can release on the above list.

Describe alternatives you've considered
Not validating the release is not optimal from security POV.

Teachability, Documentation, Adoption, Migration Strategy
Users are unaffected but benefit from the implied security that the a package provide on their distribution.

Hook `aligned_alloc`

We're currently not hooking the C11 aligned_alloc function, which means we might be missing some memory allocations.

Make memray reproducible

Bug Report

Current Behavior
memray as it stands is not reproducible 1

Input Code

The below are the reproducible tests Arch Linux infrastructure automatically perform on each package (periodically). As you see from the diffscope the Build-ID is different on the .so package.

https://reproducible.archlinux.org/api/v0/builds/267424/diffoscope
https://reproducible.archlinux.org/api/v0/builds/267424/log

Expected behavior/code
Expected behavior for this is for memray to be reproducible (byte-for-byte)

Environment

Package is build according to PKGBUILD from Arch Linux repos 2

Possible Solution

Not entirely sure but I think this is because it's build from within cython and doesn't read the Build_Date or something

Additional context/Screenshots

https://reproducible-builds.org/docs/source-date-epoch/

Expose memray reporters as part of public API

Feature Request

Is your feature request related to a problem? Please describe.

Using the memray API, via the memray.Tracker class, makes it simple to collect the raw results of profiling. However, the excellent reporters that render that data are not importable from memray.reporters, making it difficult to e.g. generate flamegraphs as part of an automated process.

The current workaround would be to use subprocess to call out to memray reporting commands, but that's needlessly heavy.

Describe the solution you'd like

The Pyinstrument API is not entirely dissimilar from the memray API, and I think it provides some good examples for how API access to renderers might work.

E.g., it would be nifty if the Tracker instance exposed a .report(reporter: MemrayReporter = TreeReporter) -> str that renders the results of profiling as HTML or text. This might require bigger changes to the Tracker API, since I believe profiling results are not kept available in memory.

Describe alternatives you've considered

A reasonable alternative would be to expose file-based reporter classes that look mostly similar to today's reporter classes. Ideally, this would involve adding a common abstraction to all of the reporter classes to handle common functionality like reading memray results into memory from file, such that each reporter class could take a string filepath or Pathlib path object for the input data, and return rendered output. This could have the benefit of drying out some of the repeated logic in the various reporter commands.

Aggregations in the high watermark are not taking into account the native frame

We are collapsing together allocations with the same python stack trace but different native stack trace. This produces flame graphs correct in the Python part but incorrectly collapsed in the native part. The problem is here:

auto alloc_it = stack_to_allocation.find(std::pair(record.frame_index, thread_id));
if (alloc_it == stack_to_allocation.end()) {
stack_to_allocation.insert(
alloc_it,
std::pair(std::pair(record.frame_index, thread_id), record));

The map from location to allocation only takes into account the frame_index but not the native_frame_id.

Failure building in Alpine Linux

I'm trying to install memray in Alpine based container. For example python:3.8-alpine.
After installing the needed packages:
apk update & apk add gcc g++ make libunwind-dev musl-dev
compilation errors appear. First, there is missing definition of pvalloc function, probably removed in gcc 10 which is available in Alpine.
I tried to add manually the function prototype in /usr/include/malloc.h - after that another errors appears:

      src/memray/_memray/hooks.h:31:30: error: redefinition of 'memray::hooks::SymbolHook<void* (*)(void*, long unsigned int, int, int, int, long int)> memray::hooks::mmap'
         31 |     FOR_EACH_HOOKED_FUNCTION(mmap64)                                                                    \
            |                              ^~~~~~
      src/memray/_memray/hooks.cpp:70:64: note: in definition of macro 'FOR_EACH_HOOKED_FUNCTION'
         70 | #define FOR_EACH_HOOKED_FUNCTION(f) SymbolHook<decltype(&::f)> f(#f, &::f);
            |                                                                ^
      src/memray/_memray/hooks.cpp:71:1: note: in expansion of macro 'MEMRAY_HOOKED_FUNCTIONS'
         71 | MEMRAY_HOOKED_FUNCTIONS
            | ^~~~~~~~~~~~~~~~~~~~~~~
      src/memray/_memray/hooks.h:30:30: note: 'memray::hooks::SymbolHook<void* (*)(void*, long unsigned int, int, int, int, long int)> memray::hooks::mmap' previously declared here
         30 |     FOR_EACH_HOOKED_FUNCTION(mmap)                                                                      \
            |                              ^~~~
      src/memray/_memray/hooks.cpp:70:64: note: in definition of macro 'FOR_EACH_HOOKED_FUNCTION'
         70 | #define FOR_EACH_HOOKED_FUNCTION(f) SymbolHook<decltype(&::f)> f(#f, &::f);
            |                                                                ^
      src/memray/_memray/hooks.cpp:71:1: note: in expansion of macro 'MEMRAY_HOOKED_FUNCTIONS'
         71 | MEMRAY_HOOKED_FUNCTIONS
            | ^~~~~~~~~~~~~~~~~~~~~~~
      In file included from src/memray/_memray/elf_utils.h:7,
                       from src/memray/_memray/hooks.h:10,
                       from src/memray/_memray/hooks.cpp:4:
      src/memray/_memray/hooks.cpp:96:1: error: redefinition of 'void* memray::intercept::mmap(void*, size_t, int, int, int, off_t)'
         96 | mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) noexcept
            | ^~~~~~
      src/memray/_memray/hooks.cpp:87:1: note: 'void* memray::intercept::mmap(void*, size_t, int, int, int, off_t)' previously defined here
         87 | mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) noexcept
            | ^~~~
      error: command '/usr/bin/gcc' failed with exit code 1

Is it possible to do something about it?

Stats reporter don't show biggest allocation in the histogram

Consider this code:

import mmap

with mmap.mmap(-1, 10000000000):
    pass

This is what we currently show in the stat reporter:

๐Ÿ“ Total allocations:
        20

๐Ÿ“ฆ Total memory allocated:
        9.313GB

๐Ÿ“Š Histogram of allocation size:
        min: 528.000B
        ----------------------------------------
        < 2.754KB  : 8 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 14.716KB : 2 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
        < 78.621KB : 0
        < 420.029KB: 0
        < 2.191MB  : 0
        < 11.707MB : 0
        < 62.545MB : 0
        < 334.139MB: 0
        < 1.743GB  : 0
        <=9.313GB  : 0
        ----------------------------------------
        max: 9.313GB

๐Ÿ“‚ Allocator type distribution:
         MALLOC: 10
         MMAP: 1

The mmap allocation is not shown in the histogram (is the only one missing).

Memray seems to modify sys.path such that local module imports fail

Bug Report

My script has a line as such:
from segtypes.segment import Segment
where segtypes.segment is a module in the current directory. Memray seems to modify the sys.path such that the cwd is not included in the list, whereas it is included during a normal python invocation. Unless I manually append the cwd to sys.path, the import fails.

Traceback (most recent call last):
  File "/home/ethteck/.local/bin/memray", line 8, in <module>
    sys.exit(main())
  File "/home/ethteck/.local/lib/python3.10/site-packages/memray/commands/__init__.py", line 124, in main
    arg_values.entrypoint(arg_values, parser)
  File "/home/ethteck/.local/lib/python3.10/site-packages/memray/commands/run.py", line 260, in run
    _run_with_file_output(args)
  File "/home/ethteck/.local/lib/python3.10/site-packages/memray/commands/run.py", line 152, in _run_with_file_output
    _run_tracker(
  File "/home/ethteck/.local/lib/python3.10/site-packages/memray/commands/run.py", line 55, in _run_tracker
    runpy.run_path(args.script, run_name="__main__")
  File "/usr/lib/python3.10/runpy.py", line 269, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/usr/lib/python3.10/runpy.py", line 96, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "split.py", line 10, in <module>
    from segtypes.segment import Segment
ModuleNotFoundError: No module named 'segtypes'

Input Code

Expected behavior/code A clear and concise description of what you expected to happen (or
code).
No ModuleNotFoundError s

Environment

  • Python(s): Python 3.10.4

Compatibility with WSL2

I used to work with memray on an ubuntu env with WSL2. When I upgraded memray from 1.0.3 to 1.2.0 I was able to launch a script with run but not to generate the flamegraph report.

Here is the associated error:

Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/lefevrej/.local/lib/python3.8/site-packages/memray/__main__.py", line 6, in <module>
    sys.exit(main())
  File "/home/lefevrej/.local/lib/python3.8/site-packages/memray/commands/__init__.py", line 124, in main
    arg_values.entrypoint(arg_values, parser)
  File "/home/lefevrej/.local/lib/python3.8/site-packages/memray/commands/common.py", line 117, in run
    self.write_report(result_path, output_file, args.show_memory_leaks, **kwargs)
  File "/home/lefevrej/.local/lib/python3.8/site-packages/memray/commands/common.py", line 76, in write_report
    reader = FileReader(os.fspath(result_path), report_progress=True)
  File "src/memray/_memray.pyx", line 503, in memray._memray.FileReader.__cinit__
  File "src/memray/_memray.pyx", line 410, in memray._memray.ProgressIndicator.__init__
AttributeError: type object 'Progress' has no attribute 'get_default_columns'

Set logging interval for run command

As I mentioned in the discussion linked below, it would be useful to set the logging interval for the run command. For example, to log memory usage every 5 seconds something like the following option could be implemented:

memray run --interval 5 file.py

Discussed in #66

Originally posted by wigging April 29, 2022
I'm using Memray to profile the memory usage of a long running Python program. The bin file is several gigabytes and I'm starting to run out of storage space. I think if I set the logging interval to a longer duration, it would decrease the size of the bin file. But the run command does not have any options for setting the logging interval. How can set the logging interval and/or decrease the size of the bin file?

Allow memray to be usable for python under MacOS

Feature Request

Is your feature request related to a problem? Please describe. A clear and concise description
of what the problem is. Ex. I have an issue when [...]

Describe the solution you'd like A clear and concise description of what you want to happen.
Add any considered drawbacks.

Describe alternatives you've considered A clear and concise description of any alternative
solutions or features you've considered.

Teachability, Documentation, Adoption, Migration Strategy If you can, explain how users will be
able to use this and possibly write out a version the docs. Maybe a screenshot or design?

Is there a timeline to allow for memray to be used by python under MacOS, I see when I try to install memray I get...

  Downloading memray-1.0.3.tar.gz (922 kB)
     โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 922.8/922.8 KB 2.2 MB/s eta 0:00:00
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error

  ร— Getting requirements to build wheel did not run successfully.
  โ”‚ exit code: 1
  โ•ฐโ”€> [16 lines of output]
      Traceback (most recent call last):
        File "/Users/klusman/Work/GIT_Projects/python-packages/BASE_SERVICES/bd_auto_detector/virtual_SIGBDAutoConfigMgr/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 363, in <module>
          main()
        File "/Users/klusman/Work/GIT_Projects/python-packages/BASE_SERVICES/bd_auto_detector/virtual_SIGBDAutoConfigMgr/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 345, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "/Users/klusman/Work/GIT_Projects/python-packages/BASE_SERVICES/bd_auto_detector/virtual_SIGBDAutoConfigMgr/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 130, in get_requires_for_build_wheel
          return hook(config_settings)
        File "/private/var/folders/d9/clqq3k317czgdkt8x8pd52dm0000gn/T/pip-build-env-98qpwlmp/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 178, in get_requires_for_build_wheel
          config_settings, requirements=['wheel'])
        File "/private/var/folders/d9/clqq3k317czgdkt8x8pd52dm0000gn/T/pip-build-env-98qpwlmp/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 159, in _get_build_requires
          self.run_setup()
        File "/private/var/folders/d9/clqq3k317czgdkt8x8pd52dm0000gn/T/pip-build-env-98qpwlmp/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 174, in run_setup
          exec(compile(code, __file__, 'exec'), locals())
        File "setup.py", line 209, in <module>
          raise RuntimeError("memray only supports Linux platforms")
      RuntimeError: memray only supports Linux platforms
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

ร— Getting requirements to build wheel did not run successfully.
โ”‚ exit code: 1
โ•ฐโ”€> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.```

Add run -c mode to allow program passed in as string

Feature Request

python3 has -c option which allows passing an arbitrary program string. This is useful for quick ad-hoc experiments in the shell (especially with --live mode).

E.g. python3 -c 'print(len([str(i) for i in range(1000000000)]))'

Would be nice if memray run had a similar mode?

Describe the solution you'd like

memray run -c could have the same logic as python3 -c

Describe alternatives you've considered

Currently the only alternative seems to put the program in a temporary script and run via memray run script.py

Perhaps there is some standard Python module one can use to run a program string so we could reuse the -m option, but I don't know of such module.

Another possible alternative is to use process substitution to avoid a temporary file, but it doesn't seem to work:

$ memray run <(echo 'print(len([str(i) for i in range(1000000000)]))')
Writing profile results into /dev/fd/memray-63.945915.bin
Could not create output file /dev/fd/memray-63.945915.bin: No such file or directory

-- fair enough, but if we try memray run -o output.bin <(echo 'print(len([str(i) for i in range(1000000000)]))'), it seems to exit immediately without any captures, as if it didn't wait for the script to finish writing.

(it works as expected for python3)

$ python3  <(echo 'print(len([str(i) for i in range(10000000)]))')
10000000

P.S. thanks for the great tool! I was seriously impressed that it 'just worked' after pip install :)

CLI ignores -o

Bug Report

CLI ignores -o and writes to a different output

$ memray run benchmarks/bench_feature_expansions.py -o /tmp/bench.bin
Writing profile results into benchmarks/memray-bench_feature_expansions.py.83352.bin
...
[memray] Successfully generated profile results.

You can now generate reports from the stored allocation records.
Some example commands to generate reports:

/path/to/.venv/bin/python -m memray flamegraph benchmarks/memray-bench_feature_expansions.py.83352.bin

It should instead write into the specified /tmp/bench.bin

Environment

  • python=3.10
  • memray=1.0.3

Ignore specified packages or paths

Feature Request

Is your feature request related to a problem? Please describe.

The memray output can be somewhat cluttered by data that I'm not interested in.

image

Describe the solution you'd like A clear and concise description of what you want to happen.

Provide a flag that would allow a user to supply a list of packages and/or paths to exclude from memray's analysis/reporting. In the above scenario, it would be great to tell memray, either through a value provided on the command line, or a pointer to a manifest file with a list of things to ignore, that I don't want it to tell me about gunicorn memory usage. Or potentially anything under /usr/local/lib/python3.7/site-packages/. This flag could be invoked either during memray run or memray <reporter> commands (or both).

Describe alternatives you've considered

Using grep (or similar tool). E.g.,:
root@28689a9cb754:/usr/src/app# memray3.7 summary memray.out.10 | grep -v gunicorn

This kinda works for some reporters, but the fidelity of the output is lost a bit. Notably, it also does not work for the flamegraph option.

image

Teachability, Documentation, Adoption, Migration Strategy If you can, explain how users will be
able to use this and possibly write out a version the docs. Maybe a screenshot or design?

root@28689a9cb754:/usr/src/app# memray3.7 summary --ignore-package gunicorn memray.out.10
or
root@28689a9cb754:/usr/src/app# memray3.7 summary --ignore-path /usr/local/lib/python3.7/site-packages/ memray.out.10
or
root@28689a9cb754:/usr/src/app# memray3.7 summary --ignore-manifest manifest.txt memray.out.10

root@28689a9cb754:/usr/src/app# cat manifest.txt
foo
bar
gunicorn
/usr/local/lib/python3.7/

Some tests for native tracking don't work in aarch64

Seems that #52 has broken the tests for native tracking on aarch64:

   def test_simple_call_chain_with_native_tracking(tmpdir, monkeypatch):
        # GIVEN
        output = Path(tmpdir) / "test.bin"
        extension_name = "multithreaded_extension"
        extension_path = tmpdir / extension_name
        shutil.copytree(TEST_NATIVE_EXTENSION, extension_path)
        subprocess.run(
            [sys.executable, str(extension_path / "setup.py"), "build_ext", "--inplace"],
            check=True,
            cwd=extension_path,
            capture_output=True,
        )

        # WHEN
        with monkeypatch.context() as ctx:
            ctx.setattr(sys, "path", [*sys.path, str(extension_path)])
            from native_ext import run_simple  # type: ignore

            with Tracker(output, native_traces=True):
                run_simple()

        # THEN
        records = list(FileReader(output).get_allocation_records())
        vallocs = [
            record
            for record in filter_relevant_allocations(records)
            if record.allocator == AllocatorType.VALLOC
        ]

        assert len(vallocs) == 1
        (valloc,) = vallocs

        (python_stack_trace,) = valloc.stack_trace()
        func, filename, line = python_stack_trace
        assert func == "test_simple_call_chain_with_native_tracking"
        assert filename.endswith(__file__)

        expected_symbols = ["baz", "bar", "foo"]
>       assert expected_symbols == [stack[0] for stack in valloc.native_stack_trace()[:3]]
E       AssertionError: assert ['baz', 'bar', 'foo'] == []
E         Left contains 3 more items, first extra item: 'baz'
E         Full diff:
E         - []
E         + ['baz', 'bar', 'foo']

tests/integration/test_native_tracking.py:112: AssertionError

Bisecting shows b6d865b as the first bad commit:

b6d865b7f481c4c67f6268483bc81f11a0833527 is the first bad commit
commit b6d865b7f481c4c67f6268483bc81f11a0833527
Author: Matthias Diener <[email protected]>
Date:   Fri Apr 22 10:36:02 2022 -0700

    Avoid inlining `exact_unwind`

    On some architectures this can't be inlined. We don't actually care
    whether or not it is inlined, only that whether it is or isn't inlined
    is consistent regardless of build flags.

    Signed-off-by: Matthias Diener <[email protected]>
    Signed-off-by: Matt Wozniski <[email protected]>

 src/memray/_memray/tracking_api.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Allow installation on OSX/Windows for creation of flamegraphs

Feature Request

Currently the package installation fails on OSX. However some parts of the package should still be usable (basically everything other than the tracker themselves).

I've had cases where the actual tracking occurred in a docker container/VM but we later want to view them in various ways on OSX, and that's not possible right now. Furthermore the setup of memray actively fails so it's hard to create projects that rely on it but are multiplatform without having to modify the setup.py significantly.

I suggest that the various tracking functions raise NotImplementedError or similar compatibility error but that general installation will still be allowed on non-Linux machines.

Add how to run with poetry scripts

Hi, thanks for this tool.

Could you add example on how to run this with poetry CLI scripts?

For example, how I imagined this would work:

poetry run memray run --live -m make -e file.txt

make is the poetry CLI command
-e some argument
file.txt another argument

Is it possible to run it this way right now?

memray fails to compile on linux-ppc64le

Bug Report

Current Behavior

memray fails to compile on linux-ppc64le:

[...]
building 'memray._memray' extension
In file included from src/memray/_memray/tracking_api.cpp:15:
src/memray/_memray/tracking_api.h: In member function 'size_t memray::tracking_api::NativeTrace::exact_unwind()':
src/memray/_memray/tracking_api.h:125:50: error: function 'size_t memray::tracking_api::NativeTrace::exact_unwind()' can never be inlined because it uses setjmp
     __attribute__((always_inline)) size_t inline exact_unwind()
                                                  ^~~~~~~~~~~~

Expected behavior/code

It should compile on ppc64le.

Environment

  • Python(s): python3.9
  • gcc: 8.3.1

Possible Solution

Remove the inline attribute from exact_unwind()

Remote monitoring configurable host

Feature Request

Configurable host for remote monitoring.

Currently the port can be configured when connecting to remote memray instances, but the host cannot be.

I have a POC done here and it seemed to be working. Was hoping to get some approval before documentation+test updates. And some guidance on versioning/release notes if approved.

Add documentation on how to use memray as an API

One of our coolest features is being able to use memray as an API. We should add documentation:

  • Add API docs with all the relevant classes and enums.
  • Add A tutorial on how to use the Tracker, FileReader and other utilities.
  • Update the README.

memray table/tree/flamegraph/summary should accept more than one result argument

Feature Request

Would be nice if I could do:

memray table dump/*.bin

instead of

memray table dump/a.bin
memray table dump/a.bin
memray table dump/c.bin

Describe the solution you'd like A clear and concise description of what you want to happen.
Add any considered drawbacks.

When more than one result is passed evaluate it in a loop.

Describe alternatives you've considered A clear and concise description of any alternative
solutions or features you've considered.

Keep requiring 3 calls.

Teachability, Documentation, Adoption, Migration Strategy If you can, explain how users will be
able to use this and possibly write out a version the docs. Maybe a screenshot or design?

Feels like by intuition it should just work, so should be easy to teach.

Should we have a `memray` entry point?

Currently we only have memray3.X as an entry point - should we also have memray? This will be more confusing for people with multiple copies of memray installed, but easier for people with only one, and it would make the usage examples from -h line up with how we actually expect it to be used.

Use api that "stack trace unavailable" in sub-thread

I had a situation that main thread has a function and its memory grows up slowly, at the same time I open a sub-thread to monitor this

import time
from threading import Thread
import memray

def hello():
    a = 'h' * 10240
    count = 0
    while count<15:
        a += a
        count += 1

def m():
    with memray.Tracker("test.bin"):
        time.sleep(2)

t = Thread(target=m, args=())
t.start()
hello()
t.join()
root@b55328e153b0:/tmp# memray stats test.bin
๐Ÿ“ Total allocations:
	2

๐Ÿ“ฆ Total memory allocated:
	480.000MB

๐Ÿ“Š Histogram of allocation size:
	min: 480.000MB
	----------------------------------------
	< 225.000PB: 1 โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡โ–‡
	< 105.5YB  : 0
	< 53084201343.8YB: 0
	< 26718158566205665280.0YB: 0
	< 13447692140004132921498664960.0YB: 0
	< 6768446389904240393238904681458040832.0YB: 0
	< 3406671275343084001598671104068678912850264064.0YB: 0
	< 1714634128676590725640136232502358247467182517297086464.0YB: 0
	< 863003782167520542775850501504234338770282486944023855555936256.0YB: 0
	<=434364110441617396824120646697741245745787014207085330508234065870585856.0YB: 0
	----------------------------------------
	max: 480.000MB

๐Ÿ“‚ Allocator type distribution:
	 MALLOC: 1

๐Ÿฅ‡ Top 5 largest allocating locations (by size):
	- <stack trace unavailable> -> 480.000MB

๐Ÿฅ‡ Top 5 largest allocating locations (by number of allocations):
	- <stack trace unavailable> -> 2

in the end, "stack trace unavailable" is printed on the screen , I can't get the detail which line consume memory

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.