コード例 #1
0
def test_sigusr2():
    """
    Sending SIGUSR2 to the process does an extra dump.
    """
    script = TEST_SCRIPTS / "sigusr2.py"
    output_dir = profile(script)

    # There are two dumps in the output directory, one for SIGUSR2, one for
    # shutdown.
    assert len(list(output_dir.iterdir())) == 2

    sigusr2, final = sorted(output_dir.glob("*/peak-memory.prof"))

    # SIGUSR2 dump only has allocations up to that point
    script = str(script)
    path1 = ((script, "<module>", 8), (numpy.core.numeric.__file__, "ones",
                                       ANY))
    path2 = ((script, "<module>", 11), (numpy.core.numeric.__file__, "ones",
                                        ANY))

    allocations_sigusr2 = get_allocations(sigusr2, direct=True)
    assert match(allocations_sigusr2, {path1: big},
                 as_mb) == pytest.approx(20, 0.1)
    with pytest.raises(MatchError):
        match(allocations_sigusr2, {path2: big}, as_mb)

    allocations_final = get_allocations(final, direct=True)
    assert match(allocations_final, {path1: big},
                 as_mb) == pytest.approx(20, 0.1)
    assert match(allocations_final, {path2: big},
                 as_mb) == pytest.approx(50, 0.1)
コード例 #2
0
def test_out_of_memory():
    """
    If an allocation is run that runs out of memory, current allocations are
    written out.
    """
    script = TEST_SCRIPTS / "oom.py"
    output_dir = profile(script, expect_exit_code=53)
    time.sleep(10)  # wait for child process to finish
    allocations = get_allocations(
        output_dir,
        [
            "out-of-memory.svg",
            "out-of-memory-reversed.svg",
            "out-of-memory.prof",
        ],
        "out-of-memory.prof",
    )

    ones = (numpy.core.numeric.__file__, "ones", ANY)
    script = str(script)
    expected_small_alloc = ((script, "<module>", 9), ones)
    toobig_alloc = ((script, "<module>", 12), ones)

    assert match(allocations, {expected_small_alloc: big},
                 as_mb) == pytest.approx(100, 0.1)
    assert match(allocations, {toobig_alloc: big},
                 as_mb) == pytest.approx(1024 * 1024 * 1024, 0.1)
コード例 #3
0
def test_malloc_in_c_extension():
    """
    Various malloc() and friends variants in C extension gets captured.
    """
    script = TEST_SCRIPTS / "malloc.py"
    output_dir = profile(script, "--size", "70")
    allocations = get_allocations(output_dir)

    script = str(script)

    # The realloc() in the scripts adds 10 to the 70:
    path = ((script, "<module>", 32), (script, "main", 28))
    assert match(allocations, {path: big},
                 as_mb) == pytest.approx(70 + 10, 0.1)

    # The C++ new allocation:
    path = ((script, "<module>", 32), (script, "main", 23))
    assert match(allocations, {path: big}, as_mb) == pytest.approx(40, 0.1)

    # C++ aligned_alloc(); not available on Conda, where it's just a macro
    # redirecting to posix_memalign.
    if not os.environ.get("CONDA_PREFIX"):
        path = ((script, "<module>", 32), (script, "main", 24))
        assert match(allocations, {path: big}, as_mb) == pytest.approx(90, 0.1)

    # Py*_*Malloc APIs:
    path = ((script, "<module>", 32), (script, "main", 25))
    assert match(allocations, {path: big}, as_mb) == pytest.approx(30, 0.1)

    # posix_memalign():
    path = ((script, "<module>", 32), (script, "main", 26))
    assert match(allocations, {path: big}, as_mb) == pytest.approx(15, 0.1)
コード例 #4
0
def test_out_of_memory_slow_leak_cgroups():
    """
    If an allocation is run that runs out of memory slowly, hitting a cgroup
    limit that's lower than system memory, current allocations are written out.
    """
    available_memory = psutil.virtual_memory().available
    script = TEST_SCRIPTS / "oom-slow.py"
    output_dir = profile(
        script,
        expect_exit_code=53,
        argv_prefix=get_systemd_run_args(available_memory),
    )
    time.sleep(10)  # wait for child process to finish
    allocations = get_allocations(
        output_dir,
        [
            "out-of-memory.svg",
            "out-of-memory-reversed.svg",
            "out-of-memory.prof",
        ],
        "out-of-memory.prof",
    )

    expected_alloc = ((str(script), "<module>", 3), )

    # Should've allocated at least a little before running out, unless testing
    # environment is _really_ restricted, in which case other tests would've
    # failed.
    assert match(allocations, {expected_alloc: big}, as_mb) > 100
コード例 #5
0
def test_jupyter(tmpdir):
    """Jupyter magic can run Fil."""
    shutil.copyfile(TEST_SCRIPTS / "jupyter.ipynb", tmpdir / "jupyter.ipynb")
    check_call(
        [
            "jupyter",
            "nbconvert",
            "--execute",
            "jupyter.ipynb",
            "--to",
            "html",
        ],
        cwd=tmpdir,
    )
    output_dir = tmpdir / "fil-result"

    # IFrame with SVG was included in output:
    with open(tmpdir / "jupyter.html") as f:
        html = f.read()
    assert "<iframe" in html
    [svg_path] = re.findall(r'src="([^"]*\.svg)"', html)
    assert svg_path.endswith("peak-memory.svg")
    assert Path(tmpdir / svg_path).exists()

    # Allocations were tracked:
    allocations = get_allocations(output_dir)
    print(allocations)
    path = (
        (re.compile("<ipython-input-3-.*"), "__magic_run_with_fil", 2),
        (re.compile("<ipython-input-2-.*"), "alloc", 4),
        (numpy.core.numeric.__file__, "ones", ANY),
    )
    assert match(allocations, {path: big}, as_mb) == pytest.approx(48, 0.1)
コード例 #6
0
def test_minus_m_minus_m():
    """
    `python -m filprofiler -m package` runs the package.
    """
    dir = TEST_SCRIPTS
    script = (dir / "malloc.py").absolute()
    output_dir = Path(mkdtemp())
    check_call(
        [
            sys.executable,
            "-m",
            "filprofiler",
            "-o",
            str(output_dir),
            "run",
            "-m",
            "malloc",
            "--size",
            "50",
        ],
        cwd=dir,
    )
    allocations = get_allocations(output_dir)
    stripped_allocations = {k[3:]: v for (k, v) in allocations.items()}
    script = str(script)
    path = ((script, "<module>", 32), (script, "main", 28))

    assert match(stripped_allocations, {path: big},
                 as_mb) == pytest.approx(50 + 10, 0.1)
コード例 #7
0
def test_jupyter(tmpdir):
    """Jupyter magic can run Fil."""
    shutil.copyfile(TEST_SCRIPTS / "jupyter.ipynb", tmpdir / "jupyter.ipynb")
    check_call(
        [
            "jupyter",
            "nbconvert",
            "--execute",
            "jupyter.ipynb",
            "--to",
            "html",
        ],
        cwd=tmpdir,
    )
    output_dir = tmpdir / "fil-result"

    # IFrame with SVG was included in output:
    with open(tmpdir / "jupyter.html") as f:
        html = f.read()
    assert "<iframe" in html
    [svg_path] = re.findall(r'src="([^"]*\.svg)"', html)
    assert svg_path.endswith("peak-memory.svg")
    assert Path(tmpdir / svg_path).exists()

    # Allocations were tracked:
    allocations = get_allocations(output_dir)
    path = (
        (re.compile(".*ipy*"), "__magic_run_with_fil", 3),
        (re.compile(".*ipy.*"), "alloc", 4),
        (numpy.core.numeric.__file__, "ones", ANY),
    )
    assert match(allocations, {path: big}, as_mb) == pytest.approx(48, 0.1)
    actual_path = None
    for key in allocations:
        try:
            match(key, path, lambda x: x)
        except MatchError:
            continue
        else:
            actual_path = key
    assert actual_path != None
    assert actual_path[0][0] != actual_path[1][0]  # code is in different cells
    path2 = (
        (re.compile(".*ipy.*"), "__magic_run_with_fil", 2),
        (numpy.core.numeric.__file__, "ones", ANY),
    )
    assert match(allocations, {path2: big}, as_mb) == pytest.approx(20, 0.1)
    # It's possible to run nbconvert again.
    check_call(
        [
            "jupyter",
            "nbconvert",
            "--execute",
            "jupyter.ipynb",
            "--to",
            "html",
        ],
        cwd=tmpdir,
    )
コード例 #8
0
def test_tabs():
    """
    Source code with tabs doesn't break SVG generation.
    """
    script = TEST_SCRIPTS / "tabs.py"
    output_dir = profile(script)
    get_allocations(output_dir)  # <- smoke test

    for svg in ["peak-memory.svg", "peak-memory-reversed.svg"]:
        svg_path = glob(str(output_dir / "*" / svg))[0]

        with open(svg_path) as f:
            svg = f.read()

        # Tabs are still there:
        assert ">\tarr1, arr2 = make_".replace(" ", "\u00a0") in svg

        # It's valid XML:
        ElementTree.fromstring(svg)
コード例 #9
0
def test_fortran():
    """
    Fil can capture Fortran allocations.
    """
    script = TEST_SCRIPTS / "fortranallocate.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    script = str(script)
    path = ((script, "<module>", 3), )

    assert match(allocations, {path: big}, as_mb) == pytest.approx(40, 0.1)
コード例 #10
0
def test_c_thread():
    """
    Allocations in C-only threads are considered allocations by the Python code
    that launched the thread.
    """
    script = TEST_SCRIPTS / "c-thread.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    script = str(script)
    alloc = ((script, "<module>", 13), (script, "main", 9))

    assert match(allocations, {alloc: big}, as_mb) == pytest.approx(17, 0.1)
コード例 #11
0
def test_minus_m():
    """
    `fil-profile -m package` runs the package.
    """
    dir = TEST_SCRIPTS
    script = (dir / "malloc.py").absolute()
    output_dir = profile("-m", "malloc", "--size", "50", cwd=dir)
    allocations = get_allocations(output_dir)
    script = str(script)
    path = ((script, "<module>", 32), (script, "main", 28))

    assert match(allocations, {path: big},
                 as_mb) == pytest.approx(50 + 10, 0.1)
コード例 #12
0
ファイル: test_endtoend.py プロジェクト: ra2003/filprofiler
def test_minus_m():
    """
    `fil-profile -m package` runs the package.
    """
    dir = Path("python-benchmarks")
    script = (dir / "malloc.py").absolute()
    output_dir = profile("-m", "malloc", "--size", "50", cwd=dir)
    allocations = get_allocations(output_dir)
    stripped_allocations = {k[3:]: v for (k, v) in allocations.items()}
    script = str(script)
    path = ((script, "<module>", 32), (script, "main", 28))

    assert match(stripped_allocations, {path: big},
                 as_mb) == pytest.approx(50 + 10, 0.1)
コード例 #13
0
def test_anonymous_mmap():
    """
    Non-file-backed mmap() gets detected and tracked.

    (NumPy uses Python memory APIs, so is not sufficient to test this.)
    """
    script = TEST_SCRIPTS / "mmaper.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    script = str(script)
    path = ((script, "<module>", 6), )

    assert match(allocations, {path: big}, as_mb) == pytest.approx(60, 0.1)
コード例 #14
0
def test_python_objects():
    """
    Python objects gets detected and tracked.

    (NumPy uses Python memory APIs, so is not sufficient to test this.)
    """
    script = TEST_SCRIPTS / "pyobject.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    script = str(script)
    path = ((script, "<module>", 1), )
    path2 = ((script, "<module>", 8), (script, "<genexpr>", 8))

    assert match(allocations, {path: big}, as_mb) == pytest.approx(34, 1)
    assert match(allocations, {path2: big}, as_mb) == pytest.approx(46, 1)
コード例 #15
0
def test_temporary_profiling(tmpdir):
    """Profiling can be run temporarily."""
    start_tracing(tmpdir)

    def f():
        arr = np.ones((1024, 1024, 4), dtype=np.uint64)  # 32MB

    f()
    stop_tracing(tmpdir)

    # Allocations were tracked:
    path = ((__file__, "f", 46), (numpy.core.numeric.__file__, "ones", ANY))
    allocations = get_allocations(tmpdir)
    assert match(allocations, {path: big}, as_mb) == pytest.approx(32, 0.1)

    # Profiling stopped:
    test_no_profiling()
コード例 #16
0
def test_thread_allocates_after_main_thread_is_done():
    """
    fil-profile tracks thread allocations that happen after the main thread
    exits.
    """
    script = TEST_SCRIPTS / "threaded_aftermain.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    import threading

    threading = (threading.__file__, "run", ANY)
    ones = (numpy.core.numeric.__file__, "ones", ANY)
    script = str(script)
    thread1_path1 = ((script, "thread1", 9), ones)

    assert match(allocations, {thread1_path1: big},
                 as_mb) == pytest.approx(70, 0.1)
コード例 #17
0
def test_temporary_profiling(tmpdir):
    """Profiling can be run temporarily."""

    # get_allocations() expects actual output in a subdirectory.
    def f():
        arr = np.ones((1024, 1024, 4), dtype=np.uint64)  # 32MB
        del arr
        return 1234

    result = profile(f, tmpdir / "output")
    assert result == 1234

    # Allocations were tracked:
    path = ((__file__, "f", 49), (numpy.core.numeric.__file__, "ones", ANY))
    allocations = get_allocations(tmpdir)
    assert match(allocations, {path: big}, as_mb) == pytest.approx(32, 0.1)

    # Profiling stopped:
    test_no_profiling()
コード例 #18
0
def run_in_ipython_shell(code_cells):
    """Run a list of strings in IPython.

    Returns parsed allocations.
    """
    InteractiveShell.clear_instance()

    shell = InteractiveShell.instance(
        display_pub_class=CapturingDisplayPublisher)
    for code in code_cells:
        shell.run_cell(code)
    InteractiveShell.clear_instance()
    html = shell.display_pub.outputs[-1]["data"]["text/html"]
    assert "<iframe" in html
    [svg_path] = re.findall('src="([^"]*)"', html)
    assert svg_path.endswith("peak-memory.svg")
    resultdir = Path(svg_path).parent.parent

    return get_allocations(resultdir)
コード例 #19
0
def test_threaded_allocation_tracking():
    """
    fil-profile tracks allocations from all threads.

    1. The main thread gets profiled.
    2. Other threads get profiled.
    """
    script = TEST_SCRIPTS / "threaded.py"
    output_dir = profile(script)
    allocations = get_allocations(output_dir)

    import threading

    threading = (threading.__file__, "run", ANY)
    ones = (numpy.core.numeric.__file__, "ones", ANY)
    script = str(script)
    h = (script, "h", 7)

    # The main thread:
    main_path = ((script, "<module>", 24), (script, "main", 21), h, ones)

    assert match(allocations, {main_path: big},
                 as_mb) == pytest.approx(50, 0.1)

    # Thread that ends before main thread:
    thread1_path1 = (
        (script, "thread1", 15),
        (script, "child1", 10),
        h,
        ones,
    )
    assert match(allocations, {thread1_path1: big},
                 as_mb) == pytest.approx(30, 0.1)
    thread1_path2 = ((script, "thread1", 13), h, ones)
    assert match(allocations, {thread1_path2: big},
                 as_mb) == pytest.approx(20, 0.1)
コード例 #20
0
def test_out_of_memory_slow_leak():
    """
    If an allocation is run that runs out of memory slowly, current allocations are
    written out.
    """
    script = TEST_SCRIPTS / "oom-slow.py"
    output_dir = profile(script, expect_exit_code=53)
    time.sleep(10)  # wait for child process to finish
    allocations = get_allocations(
        output_dir,
        [
            "out-of-memory.svg",
            "out-of-memory-reversed.svg",
            "out-of-memory.prof",
        ],
        "out-of-memory.prof",
    )

    expected_alloc = ((str(script), "<module>", 3), )

    # Should've allocated at least a little before running out, unless testing
    # environment is _really_ restricted, in which case other tests would've
    # failed.
    assert match(allocations, {expected_alloc: big}, as_mb) > 100