Full Code of parroty/exprof for AI

master d72e4247e39e cached
15 files
11.2 KB
3.7k tokens
29 symbols
1 requests
Download .txt
Repository: parroty/exprof
Branch: master
Commit: d72e4247e39e
Files: 15
Total size: 11.2 KB

Directory structure:
gitextract_a41okyq2/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── lib/
│   ├── exprof/
│   │   ├── analyzer.ex
│   │   ├── macro.ex
│   │   ├── reader.ex
│   │   └── records.ex
│   ├── exprof.ex
│   └── sample/
│       └── sample_runner.ex
├── mix.exs
├── package.exs
└── test/
    ├── exprof_test.exs
    └── test_helper.exs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/tests.yml
================================================
name: tests

on: push

jobs:
  tests:
    name: Run Tests
    runs-on: ubuntu-latest
    strategy:
      matrix:
        otp: ['19.3']
        elixir: ['1.5', '1.7']
    env:
      MIX_ENV: test
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-elixir@v1
        with:
         otp-version: ${{ matrix.otp }}
         elixir-version: ${{ matrix.elixir }}
      - uses: actions/cache@v1
        with:
         path: deps
         key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
         restore-keys: |
           ${{ runner.os }}-mix-
      - run: mix deps.get
      - run: mix test --seed 0 # disable randomization


================================================
FILE: .gitignore
================================================
/ebin
/deps
erl_crash.dump
*.ez
tmp_exprof
/_build
/doc

================================================
FILE: CHANGELOG.md
================================================
0.2.4
------
#### Changes
* Makes sure it is OK for the profiled code to send/receive messages (#13).
* Fix for warnings with elixir v1.11.
  * Add :tools to extra_applications in mix.exs (#15).

0.2.3
------
#### Changes
* Fixes issue on windows where you get File.Error permission denied.
   - Changed to use randomized file name (#11).

0.2.2
------
#### Changes
* Return block result after completion (#9).
* Link profiled process to avoid hang (#10).


================================================
FILE: LICENSE
================================================
Copyright (c) 2013-2015 parroty

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
ExProf
============

[![Build Status](https://github.com/parroty/excoveralls/workflows/tests/badge.svg)](https://github.com/parroty/exprof/actions)
[![hex.pm version](https://img.shields.io/hexpm/v/exprof.svg)](https://hex.pm/packages/exprof)
[![hex.pm downloads](https://img.shields.io/hexpm/dt/exprof.svg)](https://hex.pm/packages/exprof)

A simple code profiler for Elixir using eprof.

It provides a simple macro as a wrapper for Erlang's <a href="http://www.erlang.org/doc/man/eprof.html" target="_blank">:eprof</a> profiler.

### Install
Add `:exprof` to `deps` section of `mix.exs`.

```elixir
  def deps do
    [ {:exprof, "~> 0.2.0"} ]
  end
```

### Usage
import "ExProf.Macro", then use "profile" macro to start profiling. It
prints out results, and returns them as list of records, along with the
result of the profiled block.

```elixir
defmodule SampleRunner do
  import ExProf.Macro

  @doc "analyze with profile macro"
  def do_analyze do
    profile do
      :timer.sleep 2000
      IO.puts "message\n"
    end
  end

  @doc "get analysis records and sum them up"
  def run do
    {records, _block_result} = do_analyze
    total_percent = Enum.reduce(records, 0.0, &(&1.percent + &2))
    IO.inspect "total = #{total_percent}"
  end
end
```

### Run
An example to use in iex console.

```elixir
$ iex -S mix
..
iex(1)> SampleRunner.run
message

FUNCTION                                 CALLS      %  TIME  [uS / CALLS]
--------                                 -----    ---  ----  [----------]
'Elixir.IO':puts/2                           1   0.86     1  [      1.00]
io:o_request/3                               1   1.72     2  [      2.00]
io:put_chars/2                               1   1.72     2  [      2.00]
erlang:group_leader/0                        1   1.72     2  [      2.00]
io:request/2                                 1   2.59     3  [      3.00]
io:execute_request/2                         1   2.59     3  [      3.00]
'Elixir.SampleRunner':'-run/0-fun-0-'/0      1   2.59     3  [      3.00]
'Elixir.IO':map_dev/1                        1   3.45     4  [      4.00]
erlang:demonitor/2                           1   4.31     5  [      5.00]
io:io_request/2                              1   6.03     7  [      7.00]
io:wait_io_mon_reply/2                       1   6.90     8  [      8.00]
'Elixir.IO':puts/1                           1   8.62    10  [     10.00]
unicode:characters_to_binary/2               1  11.21    13  [     13.00]
timer:sleep/1                                1  14.66    17  [     17.00]
erlang:monitor/2                             1  31.03    36  [     36.00]
"total = 100.0"
```

### Add a Mix Task
An example to use as mix tasks.

```elixir
defmodule Mix.Tasks.Exprof do
  @shortdoc "Profile using ExProf"
  use Mix.Task
  import ExProf.Macro

  def run(_mix_args) do
    profile do: do_work(2)
  end

  defp do_work(n) do
    :timer.sleep(n * 1000)
  end
end
```


================================================
FILE: lib/exprof/analyzer.ex
================================================
defmodule ExProf.Analyzer do
  @moduledoc """
  Proivdes helper methods for operating Prof records.
  """
  import ExPrintf

  @doc """
  Returns records only have major percent values, and filter out rest of them.
  The default is to pick "80%" and it can be changed by specifying rate argument.
  """
  def get_top_percent_items(records, rate \\ 80.0) do
    sorted_records = Enum.sort(records, &(&1.percent > &2.percent))
    do_get_top_percent_items(sorted_records, rate, [])
  end

  defp do_get_top_percent_items(records, rate, acc) when length(records) == 0 or rate <= 0.0 do
    Enum.reverse(acc)
  end

  defp do_get_top_percent_items([head|tail], rate, acc) do
    do_get_top_percent_items(tail, rate - head.percent, [head|acc])
  end

  @doc """
  Print records to screen
  """
  def print(records) do
    print_header()
    Enum.each(records, &(do_print(&1)))
  end

  defp print_header do
    IO.puts "FUNCTION                                           CALLS       %  TIME  [uS / CALLS]"
    IO.puts "--------                                           -----     ---  ----  [----------]"
  end

  defp do_print(record) do
    printf("%-50s %-6d %6.2f %5d  [%10.2f]\n",
      [String.slice(record.function, 0, 50), record.calls, record.percent, record.time, record.us_per_call])
  end
end


================================================
FILE: lib/exprof/macro.ex
================================================
defmodule ExProf.Macro do
  @moduledoc """
  Provides a macro to profile a block of code.
  """

  @doc """
  A macro to specify the code block to profile.

  It spawns a new process to execute the code block for isolating the profile result.
  (ex.)

      profile do
        :timer.sleep 2000
      end

  """
  defmacro profile(do: code) do
    quote do
      ref = make_ref()
      pid = spawn_link(ExProf.Macro, :execute_profile, [fn -> unquote(code) end, ref])
      ExProf.start(pid)
      send pid, {ref, self()}

      result =
        receive do
          {^ref, result} -> result
        end

      ExProf.stop
      records = ExProf.analyze

      {records, result}
    end
  end

  @doc """
  An internal method for initiating profiling.
  """
  def execute_profile(func, ref) do
    receive do
      {^ref, sender} ->
        send sender, {ref, func.()}
        forward_other_messages(sender)
    end
  end

  defp forward_other_messages(sender) do
    receive do
      message ->
        send sender, message
        forward_other_messages(sender)
    end
  end
end


================================================
FILE: lib/exprof/reader.ex
================================================
defmodule ExProf.Reader do
  @moduledoc """
  A reader for eprof outputs.
  """

  @doc """
  Read the eprof output file and returns the parsed result of Prof records.
  """
  def read(file_name) do
    File.read!(file_name) |> String.split("\n") |> parse([])
  end

  defp parse([], acc), do: Enum.reverse(acc)
  defp parse([head|tail], acc) do
    case parse_record(head) do
      nil    -> parse(tail, acc)
      record -> parse(tail, [record|acc])
    end
  end

  defp parse_record(head) do
    case Regex.run(~r/(.+?\/[0-9])\s+(.+?)\s+(.+?)\s+(.+?)\s+\[\s+(.+?)\]/, head) do
      [_all, function, calls, percent, time, us_per_call] ->
        %Prof{function: function, calls: String.to_integer(calls), percent: String.to_float(percent),
                 time: String.to_integer(time), us_per_call: String.to_float(us_per_call)}
      nil -> nil
    end
  end
end


================================================
FILE: lib/exprof/records.ex
================================================
defmodule Prof do
  defstruct function: nil, calls: nil, percent: nil, time: nil, us_per_call: nil
end


================================================
FILE: lib/exprof.ex
================================================
defmodule ExProf do
  @moduledoc """
  Wrapper for eprof library.
  It needs to be called in start -> stop -> analyze order.
  """

  # temporary file for storing eprof output
  @tmp_prof_name 'tmp_exprof'

  @doc """
  Start the profiling for the specified pid.
  """
  def start(pid \\ self()) do
    :eprof.start
    :eprof.start_profiling([pid])
  end

  @doc """
  Stop the profiling previously started with start method call.
  """
  def stop do
    :eprof.stop_profiling
  end

  @doc """
  Analyze and output the profiling as the list of Prof records.
  It also outputs to the STDOUT.
  """
  def analyze do
    random_number = :rand.uniform(100)
    file_name = to_string(@tmp_prof_name ++ [to_string(random_number)])
    :eprof.log(file_name)
    :eprof.analyze(:total, [{:sort, :time}])
    records = ExProf.Reader.read(file_name)
    File.rm!(file_name)
    records
  end
end


================================================
FILE: lib/sample/sample_runner.ex
================================================
defmodule SampleRunner do
  import ExProf.Macro

  @doc "analyze with profile macro"
  def do_analyze do
    profile do
      :timer.sleep 2000
      IO.puts "message\n"
    end
  end

  @doc "get analysis records and sum them up"
  def run do
    {records, result} = do_analyze()
    total_percent = Enum.reduce(records, 0.0, &(&1.percent + &2))
    IO.inspect "total = #{total_percent}"
    result
  end
end


================================================
FILE: mix.exs
================================================
defmodule ExProf.Mixfile do
  use Mix.Project

  def project do
    [ app: :exprof,
      version: "0.2.4",
      elixir: "~> 1.0",
      deps: deps(),
      description: description(),
      package: package()
    ]
  end

  # Configuration for the OTP application
  def application do
    [
      extra_applications: [:tools]
    ]
  end

  # Returns the list of dependencies in the format:
  # { :foobar, "~> 0.1", git: "https://github.com/elixir-lang/foobar.git" }
  defp deps do
    [
      {:exprintf, "~> 0.2"},
      {:ex_doc, ">= 0.0.0", only: :dev}
    ]
  end

  defp description do
    """
    A simple code profiler for Elixir using eprof.
    """
  end

  defp package do
    [ maintainers: ["parroty"],
      licenses: ["MIT"],
      links: %{"GitHub" => "https://github.com/parroty/exprof"} ]
  end
end


================================================
FILE: package.exs
================================================
Expm.Package.new(name: "exprof", description: "A simple code profiler for Elixir using eprof",
                 version: "0.0.1", keywords: ["Elixir","eprof","profiler"],
                 maintainers: [[name: "parroty", email: "parroty00@gmail.com"]],
                 repositories: [[github: "parroty/exprof"]])


================================================
FILE: test/exprof_test.exs
================================================
defmodule ExprofTest do
  use ExUnit.Case, async: false
  import ExUnit.CaptureIO

  test "sample runner" do
    assert capture_io(fn ->
      assert :ok = SampleRunner.run
    end) =~ ~r/FUNCTION\s+CALLS/m
  end

  @tag timeout: 1000
  test "abort on exit" do
    Process.flag(:trap_exit, true)

    pid = spawn_link(fn ->
      import ExProf.Macro

      profile do
        Process.exit(self(), :kill)
      end
    end)

    assert_receive {:EXIT, ^pid, :killed}, 500
  end
end


================================================
FILE: test/test_helper.exs
================================================
ExUnit.start
Download .txt
gitextract_a41okyq2/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── lib/
│   ├── exprof/
│   │   ├── analyzer.ex
│   │   ├── macro.ex
│   │   ├── reader.ex
│   │   └── records.ex
│   ├── exprof.ex
│   └── sample/
│       └── sample_runner.ex
├── mix.exs
├── package.exs
└── test/
    ├── exprof_test.exs
    └── test_helper.exs
Download .txt
SYMBOL INDEX (29 symbols across 8 files)

FILE: lib/exprof.ex
  class ExProf (line 1) | defmodule ExProf
    method start (line 13) | def start(pid \\ self()) do
    method stop (line 21) | def stop do
    method analyze (line 29) | def analyze do

FILE: lib/exprof/analyzer.ex
  class ExProf.Analyzer (line 1) | defmodule ExProf.Analyzer
    method get_top_percent_items (line 11) | def get_top_percent_items(records, rate \\ 80.0) do
    method do_get_top_percent_items (line 20) | defp do_get_top_percent_items([head|tail], rate, acc) do
    method print (line 27) | def print(records) do
    method print_header (line 32) | defp print_header do
    method do_print (line 37) | defp do_print(record) do

FILE: lib/exprof/macro.ex
  class ExProf.Macro (line 1) | defmodule ExProf.Macro
    method execute_profile (line 39) | def execute_profile(func, ref) do
    method forward_other_messages (line 47) | defp forward_other_messages(sender) do

FILE: lib/exprof/reader.ex
  class ExProf.Reader (line 1) | defmodule ExProf.Reader
    method read (line 9) | def read(file_name) do
    method parse (line 13) | defp parse([], acc), do: Enum.reverse(acc)
    method parse (line 14) | defp parse([head|tail], acc) do
    method parse_record (line 21) | defp parse_record(head) do

FILE: lib/exprof/records.ex
  class Prof (line 1) | defmodule Prof

FILE: lib/sample/sample_runner.ex
  class SampleRunner (line 1) | defmodule SampleRunner
    method do_analyze (line 5) | def do_analyze do
    method run (line 13) | def run do

FILE: mix.exs
  class ExProf.Mixfile (line 1) | defmodule ExProf.Mixfile
    method project (line 4) | def project do
    method application (line 15) | def application do
    method deps (line 23) | defp deps do
    method description (line 30) | defp description do
    method package (line 36) | defp package do

FILE: test/exprof_test.exs
  class ExprofTest (line 1) | defmodule ExprofTest
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
  {
    "path": ".github/workflows/tests.yml",
    "chars": 731,
    "preview": "name: tests\n\non: push\n\njobs:\n  tests:\n    name: Run Tests\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n       "
  },
  {
    "path": ".gitignore",
    "chars": 55,
    "preview": "/ebin\n/deps\nerl_crash.dump\n*.ez\ntmp_exprof\n/_build\n/doc"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 456,
    "preview": "0.2.4\n------\n#### Changes\n* Makes sure it is OK for the profiled code to send/receive messages (#13).\n* Fix for warnings"
  },
  {
    "path": "LICENSE",
    "chars": 1056,
    "preview": "Copyright (c) 2013-2015 parroty\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this so"
  },
  {
    "path": "README.md",
    "chars": 2926,
    "preview": "ExProf\n============\n\n[![Build Status](https://github.com/parroty/excoveralls/workflows/tests/badge.svg)](https://github."
  },
  {
    "path": "lib/exprof/analyzer.ex",
    "chars": 1300,
    "preview": "defmodule ExProf.Analyzer do\n  @moduledoc \"\"\"\n  Proivdes helper methods for operating Prof records.\n  \"\"\"\n  import ExPri"
  },
  {
    "path": "lib/exprof/macro.ex",
    "chars": 1081,
    "preview": "defmodule ExProf.Macro do\n  @moduledoc \"\"\"\n  Provides a macro to profile a block of code.\n  \"\"\"\n\n  @doc \"\"\"\n  A macro to"
  },
  {
    "path": "lib/exprof/reader.ex",
    "chars": 870,
    "preview": "defmodule ExProf.Reader do\n  @moduledoc \"\"\"\n  A reader for eprof outputs.\n  \"\"\"\n\n  @doc \"\"\"\n  Read the eprof output file"
  },
  {
    "path": "lib/exprof/records.ex",
    "chars": 103,
    "preview": "defmodule Prof do\n  defstruct function: nil, calls: nil, percent: nil, time: nil, us_per_call: nil\nend\n"
  },
  {
    "path": "lib/exprof.ex",
    "chars": 888,
    "preview": "defmodule ExProf do\n  @moduledoc \"\"\"\n  Wrapper for eprof library.\n  It needs to be called in start -> stop -> analyze or"
  },
  {
    "path": "lib/sample/sample_runner.ex",
    "chars": 410,
    "preview": "defmodule SampleRunner do\n  import ExProf.Macro\n\n  @doc \"analyze with profile macro\"\n  def do_analyze do\n    profile do\n"
  },
  {
    "path": "mix.exs",
    "chars": 819,
    "preview": "defmodule ExProf.Mixfile do\n  use Mix.Project\n\n  def project do\n    [ app: :exprof,\n      version: \"0.2.4\",\n      elixir"
  },
  {
    "path": "package.exs",
    "chars": 313,
    "preview": "Expm.Package.new(name: \"exprof\", description: \"A simple code profiler for Elixir using eprof\",\n                 version:"
  },
  {
    "path": "test/exprof_test.exs",
    "chars": 481,
    "preview": "defmodule ExprofTest do\n  use ExUnit.Case, async: false\n  import ExUnit.CaptureIO\n\n  test \"sample runner\" do\n    assert "
  },
  {
    "path": "test/test_helper.exs",
    "chars": 13,
    "preview": "ExUnit.start\n"
  }
]

About this extraction

This page contains the full source code of the parroty/exprof GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (11.2 KB), approximately 3.7k tokens, and a symbol index with 29 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!