=begin pod

=head1 NAME

MoarVM::Profile - Raku interface to MoarVM profiles

=head1 SYNOPSIS

=begin code :lang<raku>
use MoarVM::Profile;
my $profile = MoarVM::Profile(
  'sub foo($a) { $a * $a }; foo($_) for ^1000000'
);

say $profile;
=end code
=begin output
MoarVM Profiler Results at 2026-01-16T21:00:43+01:00
================================================================================
sub foo($a) { $a * $a }; foo($_) for ^1000000

Time Spent
================================================================================
The profiled code ran for 135.22ms.
Of this, 29.48ms were spent in garbage collection (that's 21.80%).
The dynamic optimizer was active for 0.54% of the program's run time.

Call Frames
================================================================================
In total, 5742 call frames were entered and exited by the profiled code.
Inlining eliminated the need to create 2996952 call frames (that's 99.81%).
5293 call frames were interpreted, 2997401 were specialized (99.82%).

Garbage Collection
================================================================================
The profiled code did 19 garbage collections.
The average nursery collection time was 1.55ms.
Scalar replacement eliminated 999137 allocations (that's 33.28%).

Dynamic Optimization
================================================================================
2997401 optimized frames were seen.
There was one On Stack Replacement performed.

157 Routines (showing 5 with most CPU usage)
================================================================================
  Entries    Inclusive    Exclusive   Exec  Name
  1000000      33.46%       20.14%   spesh  infix:<*>
               45.25ms      27.24ms         SETTING::src/core.c/Int.rakumod:348

  1000000      50.10%       16.55%   spesh  foo
               67.74ms      22.38ms         -e:1

  1000000      65.74%       15.62%   spesh  (block)
               88.90ms      21.12ms         -e:1

       57       0.28%        0.14%  interp  (block)
                0.37ms       0.19ms         src/vm/moar/dispatchers.nqp:1125

      166       0.29%        0.13%  interp  find_method
                0.40ms       0.17ms         src/Perl6/Metamodel/MROBasedMethodDispatch.nqp:13

24 Types (showing 5 most allocated)
================================================================================
Allocations  Name / Routines
    1999981  Int at 2 call sites:
     999996  infix:<*> SETTING::src/core.c/Int.rakumod:348
     999985  -e:1

        874  Scalar at 7 call sites:
        863  foo -e:1
          3  POPULATE SETTING::src/core.c/Exception.rakumod:1092
          2  backtrace SETTING::src/core.c/Exception.rakumod:10

        580  BOOTCapture at 21 call sites:
        128  NQP::src/core/dispatchers.nqp:5
        122  src/vm/moar/dispatchers.nqp:1452
         76  src/vm/moar/dispatchers.nqp:3431

        463  BOOTTracked at 21 call sites:
         77  src/vm/moar/dispatchers.nqp:3925
         66  src/vm/moar/dispatchers.nqp:3431
         66  NQP::src/core/dispatchers.nqp:273

        451  BOOTHash at 24 call sites:
        332  find_method src/Perl6/Metamodel/MROBasedMethodDispatch.nqp:13
         24  capture SETTING::src/core.c/Parameter.rakumod:297
         16  slurpy SETTING::src/core.c/Parameter.rakumod:288

19 Garbage Collections (showing 5 slowest)
================================================================================
    time seq# F  started  retained  promoted     freed    gen2
  2.84ms    1     3.94ms      18Kb     786Kb    3290Kb    7545 roots

  2.61ms   16   108.07ms       80b              4095Kb      13 roots

  2.02ms    2    11.84ms       80b      18Kb    4077Kb     131 roots

  1.85ms   17   116.64ms       80b              4095Kb      13 roots

  1.52ms   18   124.06ms       80b              4095Kb      13 roots

=end output

or use the installed script B<mvm-profile>:

=begin output
$ mvm-profile 'sub foo($a) { $a * $a }; foo($_) for ^1000000'
MoarVM Profiler Results at 2026-01-16T21:00:43+01:00
================================================================================
sub foo($a) { $a * $a }; foo($_) for ^1000000

Time Spent
...
=end output

=head1 DESCRIPTION

The C<MoarVM::Profile> distribution provides a Raku interface for the
information provided by the MoarVM/Rakudo code profiling information.
It is intended to be used in generally applicable applications, but
also for ad-hoc profiling situations and benchmarking.

=head1 MoarVM::Profile

The main class provided by this distribution is the C<MoarVM::Profile>
class.  Instantiation happens with the C<.new> method, which takes
one positional argument, and several named arguments.

=head2 Positional argument

One of the following:

=head3 string with code to be executed

Creates the C<MoarVM::Profile> object with the profile information
resulting from executing the given code, without creating a permanent
SQLite database file.

If the string does not contain any whitespace, and interpreting the
string as a path yields a file that exists, then it will be assumed
to be a script with executable code.

=head3 path to script to execute

Creates the C<MoarVM::Profile> object with the profile information
resulting from executing the code in the given C<IO::Path>.  If the
named argument C<:create> is specified with a true value, a permanent
SQLite database file will be created with the ".db" extension.

=head3 path to pre-generated file with SQL statements (extension: .sql)

Creates the C<MoarVM::Profile> object from the pre-generated SQL
in the given C<IO::Path>.  If the named argument C<:create> is specified
with a true value, a permanent SQLite database file will be created with
the ".db" extension.

=head3 path to pre-generated database file (extension: .db)

Creates the C<MoarVM::Profile> object from the given SQLite database.

=head3 a C<DB::SQLite> object with a profile database

Creates the C<MoarVM::Profile> object from the given C<DB::SQLite>
object.

=head2 Named arguments

=head3 :keep

If specified with a trueish value, will create a permanent copy of
the underlying database (if applicable).  If the value is a string,
then it will be assumed to be the name of the directory in which to
store the permanent copy of the database.  If a C<Bool>, then the
current directory will be assumed.

The basename of the database is the number of nano-seconds since
epoch.  The extension is C<.db>.

=head3 :type

The type of profile to make: supported are:

=item profile (default)

Profile all execution, including compilation.

=item profile-compile

Just profile the compilation of the code.

=head2 Methods

=head2 allocations-overview

Returns the C<MoarVM::Profile::AllocationsOverview> object associated
with this profile.

=head3 calls

Returns a C<List> of C<MoarVM::Profile::Call> objects, where the index
of the object is the same as the C<.id> of the object.

=begin code :lang<raku>
my $call := $profile.calls[$call-id];
=end code

=head2 calls-overview

Returns the C<MoarVM::Profile::CallsOverview> object associated with this
profile.

=head3 db

The C<DB::SQLite> database handle connected to the database associated
with the profile.

=head3 deallocations

Returns a C<List> of C<MoarVM::Profile::Deallocation> objects, one for
each deallocation having been done.

=begin code :lang<raku>
.say for $profile.deallocations;
=end code

=head3 files

Returns a sorted C<List> of "paths" found in this profile.  Note these
can have special path indicators such as "SETTING::" and "NQP::", so
there's no direct path to an actual file (see C<.ios>).

=head3 gc-overview

Returns a C<MoarVM::Profile::GCOverview> object.  Please note that this
will only contain sensible information if at least one garbage collection
has been done.

=begin code :lang<raku>
say $profile.gc-overview;
=end code

=head3 gcs

Returns a C<List> of C<MoarVM::Profile::GC> objects, one for each
garbage collection run having been done.

=begin code :lang<raku>
.say for $profile.gcs;
=end code

=head3 ios

Returns a C<List> of C<IO::Path> objects for all the files, in the
same order as the C<.files> method.

=head3 names

Returns a sorted C<List> of routine names found in this profile.

=head3 overviews

Returns a C<List> of C<MoarVM::Profile::Overview> objects, one for
each thread.

=begin code :lang<raku>
.say for $profile.overviews;
=end code

Can also be called with a thread ID, to just return the
C<MoarVM::Profile::Overview> object for that thread.

=begin code :lang<raku>
say $profile.overviews[1];
=end code

=head3 query

Perform a SQL query on the database associated with the profile.

=head3 report

Produce a report about this profile, allowing for these named arguments:

=item :routines - # of routines to show (default: 5)
=item :types - # of types with allocations to show (default: 5)
=item :routines-per-type - # of routines per type (default: 3)
=item :gcs - # of garbage collections (default: 5)

=head3 routines

Returns a C<List> of C<MoarVM::Profile::Routine> objects, where the index
of the object is the same as the C<.id> of the object.

=begin code :lang<raku>
my $routine := $profile.routines[$routine-id];
=end code

Optionally takes two named arguments:

=item :name - the name to match exactly
=item :file - the name of the file to match exactly
=item :line - the line number in the file to match exactly

=begin code :lang<raku>
my @routines := $profile.routines(:name<foo>, :file<-e>);
=end code

Note that this also returns a C<List>, as multi subroutines /
methods / tokens / rules / regex result in multiple matches.

=head3 target

The target with which this object was created: either a path
(string or C<IO::Path>), or code to be executed.

=head3 types

Returns a C<List> of C<MoarVM::Profile::Type> objects, where the index
of the object is the same as the C<.id> of the object.

=begin code :lang<raku>
my $type := $profile.types[$type-id];
=end code

=head3 user-files

Returns a sorted C<List> of absolute paths of user files found in this
profile.

=head3 user-ios

Returns a C<List> of C<IO::Path> objects for all the user files, in
the same order as the C<.user-files> method.

=head3 user-names

Returns a sorted C<List> of user routine names found in this profile.

=head1 SUBTYPES

This distribution provides a number of C<MoarVM::Profile::xxx> subtypes
who share a number of methods.

Most methods return a native C<int>: some obviously do not, such as
C<.name> and C<.file> methods, which return a native C<str>.

Methods that indicate a start / end time or interval, are in B<nano>
seconds.

=head2 Methods

=head3 columns

String with all columns concatenated of associated table, or C<Nil>
if the class works on a compound SQL statement.

=head3 select

String with a SQL statement to select all columns of the associated
table(s).

=head3 table

String with the name of the associated table, or C<Nil> if the class
works on a compound SQL statement.

=head1 MoarVM::Profile::Allocation

An object containing allocation information about a given
C<MoarVM::Profile::Call> and a given C<MoarVM::Profile::Type>.

=head2 Methods

=head3 call-id

ID of the associated C<MoarVM::Profile::Call> object.

=head3 count

The number of allocations made for the associated C<MoarVM::Profile::Call>
and C<MoarVM::Profile::Type> object.

=head3 jit

The number of allocations made for the associated C<MoarVM::Profile::Call>
and C<MoarVM::Profile::Type> object while the code in fact was JITted.

=head3 spesh

The number of allocations made for the associated C<MoarVM::Profile::Call>
and C<MoarVM::Profile::Type> object while the code in fact was speshed.

=head3 replaced

The number of allocations that did B<not> needed to be made for the
associated C<MoarVM::Profile::Call> and C<MoarVM::Profile::Type> object
because a scalar replacement made it unnecessary to do an allocation.

=head3 type-id

ID of the associated C<MoarVM::Profile::Type> object.

=head1 MoarVM::Profile::AllocationOverview

An object containing summary information about all calls in this profile.

=head2 Methods

=item allocated
=item counted
=item jitted
=item replaced
=item speshed

=head1 MoarVM::Profile::Call

An object containing information about a given call to a
C<MoarVM::Profile::Routine>.

=head2 Methods

=item deopt-all
=item deopt-one
=item entries
=item highest-child-id
=item inlined-entries
=item jit-entries
=item osr
=item rec-depth
=item spesh-entries

=head3 allocations

Returns a C<List> with C<MoarVM::Profile::Allocation> objects for this
routine.

=head3 ancestry

Returns a C<List> with C<MoarVM::Profile::Call> objects of the parents
of this call.

=head3 first-entry-time

The time this call was first made.

=head3 exclusive-time

The number of nano seconds spent in the routine called B<without>
including the time spent in any calls made inside that routine.

=head3 id

The ID of this call.

=head3 inclusive-time

The number of nano seconds spent in the routine called
B<including> the time spent in any calls made inside that routine.

=head3 parent

Returns the C<MoarVM::Profile::Call> object of the parent of this call,
or C<Nil> if no parent could be found.

=head3 parent-id

Returns the C<id> of the parent of this call.

=head3 routine

The C<MoarVM::Profile::Routine> object that was called.

=head3 routine-id

The ID of the C<MoarVM::Profile::Routine> object that was called.

=head1 MoarVM::Profile::CallsOverview

An object containing summary information about all calls in this profile.

=head2 Methods

=item deopt-all-total
=item deopt-one-total
=item entries-total
=item inlined-entries-total
=item jit-entries-total
=item osr-total
=item spesh-entries-total

=head1 MoarVM::Profile::Deallocation

An object containing information about a de-allocation of a given
garbage collect sequence number, the thread performing the garbage
collection, and the type being garbage collected.

=head2 Methods

=item gc-seq-num
=item gc-thread-id
=item gen2
=item nursery-fresh
=item nursery-seen

=head3 type-id

The ID of the C<MoarVM::Profile::Type> object that was deallocated.

=head1 MoarVM::Profile::GC

An object containing information about a garbage collection run.

=head2 Methods

=item cleared-bytes
=item gen2-roots
=item promoted-bytes
=item retained-bytes
=item stolen-gen2-roots

=head3 full

B<1> if this was a full garbage collection, else B<0>.

=head3 report

Produce a report about this C<GC>, accepts these named arguments:

=item bold - bolden if possible (default: not)
=item header - show a header (default: not)

=head3 responsible

The ID of the thread that initiated the garbage collection.

=head3 sequence-num

Basically the ID of this garbage collection (starts at 1).

=head3 start-time

The time this garbage collection was started.

=head3 thread-id

The ID of the thread doing the garbage collection.

=head3 time

The time used to perform the garbage collection.

=head1 MoarVM::Profile::GCOverview

An object containing overview information about garbage collections
done in this profile.

=head2 methods

=head3 avg-major-time

The average time spent on a full garbage collection (B<0> if none
were done).

=head3 avg-minor-time

The average time spent on a partial garbage collection (B<0> if none
were done).

=head3 max-major-time

The maximum time spent on a full garbage collection (B<0> if none
were done).

=head3 max-minor-time

The maximum time spent on a partial garbage collection (B<0> if none
were done).

=head3 min-major-time

The minumum time spent on a full garbage collection (B<0> if none
were done).

=head3 min-minor-time

The minumum time spent on a partial garbage collection (B<0> if none
were done).

=head3 total-major

The total time spent on full garbage collections (B<0> if none were
done).

=head3 total-minor

The total time spent on partial garbage collections (B<0> if none were
done).

=head1 MoarVM::Profile::Overview

An object containing overview information about this profile.

=head2 Methods

=item root-node

=head3 first-entry-time

The time profiling was started.

=head3 parent-thread-id

The ID of the thread that initiated (B<0> if no parent thread known).

=head3 spesh-time

The amount of time spent by spesh.

=head3 thread-id

The ID of the thread of this information.

=head3 total-time

Total execution time for the program that created this profile.

=head1 MoarVM::Profile::Routine

The C<MoarVM::Routine> object encapsulates the information about a block
that has been executed at least once.  A such, the name "Routine" is a bit
of a misnomer, "Block" would have been better probably.  But the naming
of these modules is following the names of the SQL tables provided, so
"Routine" it is.

How the C<MoarVM::Routine> object is created, is an implementation detail
and as such not documented.

=head2 Methods

=head3 calls

Returns a C<List> of C<MoarVM::Profile::Call> objects for each call from
a different location made to this C<Block>.

=head3 deopt-all

The number of times this C<Block> has seen a global de-optimization.

=head3 deopt-one

The number of times this C<Block> has seen a local de-optimization.

=head3 entries

The total number of times this C<Block> was being called.

=head3 exclusive-time

The time spent in execution of this C<Block> alone.

=head3 file

The file in which the C<Block> has been defined.  Note this can have
special path indicators such as "SETTING::" and "NQP::", so there's no
direct path to an actual file.

=head3 file-line

Concatenation of the C<.file> and C<.line> methods with a colon
inbetween.  Intended to provide a consistent string representation.

=head3 id

The numerical ID of the C<Block> in this profile.

=head3 inclusive-time

The time spent in execution of this C<Block>, including time spent
in any calls that were made in this C<Block>.

=head3 inlined-entries

The number of calls that were made with this C<Block> inlined.

=head3 io

The normalized C<IO::Path> of the file in which this C<Block> was
defined.

=head3 is-block

Returns C<True> if the C<Block> is not a C<Routine>.

=head3 is-core

Returns C<True> if the C<Block> is part of the Rakudo core.

=head3 is-user

Returns C<True> if the C<Block> is user-supplied code.

=head3 jit-entries

The number of calls that were made with this C<Block> JITted.

=head3 line

The line number in which the C<Block> has been defined (if available).
B<-1> if no line number could be obtained (which is typical of some low
level code blocks).

=head3 lines-around

=begin code :lang<raku>
say $routine.lines-around;      # 3 lines before / after
say $routine.lines-around(10);  # 10 lines before / after
=end code

If there is C<.source> available, then this will by default return
the source code B<3> lines before and after the line in which the
C<Block> begins to be defined.  Another number of lines can be
specified with the positional argument.

If there is no C<.source> available, C<Nil> will be returned.

=head3 name

The name of the C<Block>, "(block)" if there is no name, implying this
is some type of non-C<Routine> C<Block>.

=head3 name-file-line

Concatenation of the C<.name> and C<.fileline> methods, with a colon
inbetween.  Intended to provide a consistent string representation.

=head3 osr

Returns B<1> if this C<Block> had an Online Stack Replacement.

=head3 report

Produce a report about this C<Block>, accepts these named arguments:

=item bold - bolden if possible (default: not)
=item header - show a header (default: not)

=head3 site-count

The number of places from which this C<Block> was being called.

=head3 source

Returns the complete source of the file of this C<Block> if available.
If the profile was created with just code, then that will returned.  Else
C<Nil> will be returned.

=head3 spesh-entries

The number of calls that were made with this C<Block> speshed.

=head1 MoarVM::Profile::Type

The C<MoarVM::Type> object encapsulates the information about a type
(class, enum, subset) that has been accessed at least once.

=head2 Methods

=head3 allocated

Returns the total number of allocations done for this type in this
profile.

=head3 allocated-by-routine

Returns a C<Bag> with the number of allocations done per
C<MoarVM::Profile::Routine> object.

=head3 allocations

Returns a C<Map> with C<MoarVM::Profile::Allocation> objects for this
type, keyed by their C<.call-id>.

=head3 calls

Returns a C<List> with C<MoarVM::Profile::Call> objects of the C<Block>s
in which allocations for this type were made.

=head3 count

The total number of allocations made for the this type.

=head3 extra-info

A C<Map> with extra information about this type.

=head3 id

The numerical ID of the type in this profile.

=head3 jit

The total number of allocations made for this type while the code in
fact was JITted.

=head3 name

The name of the this type.

=head3 replaced

The total number of allocations that did B<not> needed to be made
for the type because a scalar replacement made it unnecessary to do
an allocation.

=head3 report

Produce a report about this C<Type>, accepts these named arguments:

=item bold - bolden if possible (default: not)
=item header - show a header (default: not)

=head3 spesh

The total number of allocations made for this type while the code
in fact was speshed.

=head1 EXPORTED SUBROUTINES

=head2 file2io

=begin code :lang<raku>
say file2io("SETTING::src/core.c/Int.rakumod");
=end code

Looks at the C<file> value (as returned by C<MoarVM::Profile::Routine.file>
method or the C<MoarVM::Profile.files> method) and attempts to convert this
to an C<IO::Path> of an existing path, taking into account path indicators
such as "SETTING::" and "NQP::".

If no valid path is found (like for "-e"), a C<Failure> is returned.

=head1 CREDITS

The SQL used in this module have been mostly copied from the
L<moarperf|https://github.com/timo/moarperf/blob/master/lib/ProfilerWeb.pm6>
repository by I<Timo Paulssen>.

=head1 AUTHOR

Elizabeth Mattijsen <liz@raku.rocks>

Source can be located at: https://github.com/lizmat/MoarVM-Profile . Comments
and Pull Requests are welcome.

If you like this module, or what I'm doing more generally, committing to a
L<small sponsorship|https://github.com/sponsors/lizmat/>  would mean a great
deal to me!

=head1 COPYRIGHT AND LICENSE

Copyright 2026 Elizabeth Mattijsen

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

# vim: expandtab shiftwidth=4
