<commit_before># An object oriented interface to the data structure as returned by
# nqp::mvmendprofile, when started with mvmstartprofile({:instrumented})

# We need NQP here, duh!
use nqp;

# Simple role that maps a set of given keys onto a hash, so that we need to
# do the minimal amount of work to convert the data structure to a full-blown
# object hierarchy.
role OnHash[@keys] {
    has %.hash;

    for @keys -> $key {
        $?CLASS.^add_method($key, { .hash.AT-KEY($key) } )
    }

    method new(%hash) { self.bless(:%hash) }

    method !mogrify-to-object(\the-class, \key --> Nil) {
        with %!hash{key} {
            $_ = the-class.new($_) unless $_ ~~ the-class;
        }
        default {
            $_ = the-class.new({});
        }
    }

    method !mogrify-to-list(\the-class, \key --> Nil) {
        with %!hash{key} {
            $_ = @$_.map( {
                 $_ ~~ the-class ?? $_ !! the-class.new($_)
            } ).list;
        }
        default {
            $_ = ()
        }
    }
}

class Profile::Allocation does OnHash[<
  count
  id
>] { }

class Profile::Callee does OnHash[<
  allocations
  entries
  exclusive_time
  file
  first_entry_time
  id
  inclusive_time
  line
  name
>] {

    method TWEAK(:@allocations --> Nil) {
        self!mogrify-to-list(Profile::Allocation, 'allocations');
    }
}

class Profile::CallGraph does OnHash[<
  allocations
  callees
  entries
  exclusive_time
  file
  first_entry_time
  id
  inclusive_time
  line
  name
>] {

    method TWEAK(:@allocations, :@callees --> Nil) {
        self!mogrify-to-list(Profile::Allocation, 'allocations');
        self!mogrify-to-list(Profile::Callee,     'callees'    );
    }
}

class Profile::Deallocation does OnHash[<
  id
  nursery_fresh
  nursery_seen
>] { }

class Profile::GC does OnHash[<
  cleared_bytes
  deallocations
  full
  gen2_roots
  promoted_bytes
  promoted_bytes_unmanaged
  responsible
  retained_bytes
  sequence
  start_time
  time
>] {

    method TWEAK(:@deallocations --> Nil) {
        self!mogrify-to-list(Profile::Deallocation, 'deallocations');
    }
}

class Profile::Thread does OnHash[<
  call_graph
  gcs
  id
  parent
  spesh_time
  start_time
  total_time
>] {

    method TWEAK(--> Nil) {
        self!mogrify-to-object(Profile::CallGraph, 'call_graph');
        self!mogrify-to-list(Profile::GC, 'gcs');
    }
}

class Profile {
    has @.global  is required;
    has @.threads is required;

    method new(@raw) {
        self.bless(
          global  => @raw[0],
          threads => @raw.skip.map( { Profile::Thread.new($_) } ).list,<commit_msg>Some tweaks in the Profile WIP<commit_after># An object oriented interface to the data structure as returned by
# nqp::mvmendprofile, when started with mvmstartprofile({:instrumented})

# We need NQP here, duh!
use nqp;

# Simple role that maps a set of given keys onto a hash, so that we need to
# do the minimal amount of work to convert the data structure to a full-blown
# object hierarchy.
role OnHash[@keys] {
    has %.hash;

    for @keys -> $key {
        $?CLASS.^add_method($key, { .hash.AT-KEY($key) } )
    }

    method new(%hash) { self.bless(:%hash) }

    method !mogrify-to-object(\the-class, \key --> Nil) {
        if %!hash{key} -> $hash {
            %!hash{key} = the-class.new($hash) unless $hash ~~ the-class;
        }
        else {
            %!hash{key} = the-class.new({});
        }
    }

    method !mogrify-to-list(\the-class, \key --> Nil) {
        if %!hash{key} -> @list {
            %!hash{key} = @list.map( {
                $_ ~~ the-class ?? $_ !! the-class.new($_)
            } ).list;
        }
        else {
            %!hash{key} = ()
        }
    }
}

class Profile::Allocation does OnHash[<
  count
  id
>] { }

class Profile::Callee does OnHash[<
  allocations
  entries
  exclusive_time
  file
  first_entry_time
  id
  inclusive_time
  line
  name
>] {

    method TWEAK(:@allocations --> Nil) {
        self!mogrify-to-list(Profile::Allocation, 'allocations');
    }
}

class Profile::CallGraph does OnHash[<
  allocations
  callees
  entries
  exclusive_time
  file
  first_entry_time
  id
  inclusive_time
  line
  name
>] {

    method TWEAK(:@allocations, :@callees --> Nil) {
        self!mogrify-to-list(Profile::Allocation, 'allocations');
        self!mogrify-to-list(Profile::Callee,     'callees'    );
    }
}

class Profile::Deallocation does OnHash[<
  id
  nursery_fresh
  nursery_seen
>] { }

class Profile::GC does OnHash[<
  cleared_bytes
  deallocs
  full
  gen2_roots
  promoted_bytes
  promoted_bytes_unmanaged
  responsible
  retained_bytes
  sequence
  start_time
  time
>] {

    method TWEAK(:@deallocations --> Nil) {
        self!mogrify-to-list(Profile::Deallocation, 'deallocs');
    }
}

class Profile::Thread does OnHash[<
  call_graph
  gcs
  id
  parent
  spesh_time
  start_time
  total_time
>] {

    method TWEAK(--> Nil) {
        self!mogrify-to-object(Profile::CallGraph, 'call_graph');
        self!mogrify-to-list(Profile::GC, 'gcs');
    }
}

class Profile {
    has @.global  is required;
    has @.threads is required;

    method new(@raw) {
        self.bless(
          global  => @raw[0],
          threads => @raw.skip.map( { Profile::Thread.new($_) } ).list,
{'fraction_non_alphanumeric': 0.13372534142640363, 'fraction_numerical': 0.0007587253414264037, 'mean_word_length': 2.4667981591058514, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3342223', 'n_tokens_mistral': 1873, 'n_tokens_neox': 1831, 'n_words': 413}