File: TrackObjectRelease.pm

package info (click to toggle)
libur-perl 0.470%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 7,184 kB
  • sloc: perl: 61,813; javascript: 255; xml: 108; sh: 13; makefile: 9
file content (106 lines) | stat: -rw-r--r-- 3,342 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package UR::Namespace::Command::Test::TrackObjectRelease;

use strict;
use warnings;

use UR;
our $VERSION = "0.47"; # UR $VERSION;
use IO::File;

class UR::Namespace::Command::Test::TrackObjectRelease {
    is => 'UR::Namespace::Command::Base',
    has => [
        file => { is => 'Text', doc => 'pathname of the input file' },
    ],
};

sub help_brief { 'Parse the data produced by UR_DEBUG_OBJECT_RELEASE and report possible memory leaks' };

sub help_synopsis { 
"ur test track-object-release --file /path/to/text.file > /path/to/results"
}

sub help_detail {
"When a UR-based program is run with the UR_DEBUG_OBJECT_RELEASE environment
variable set to 1, it will emit messages to STDERR describing the various
stages of releasing an object.  This command parses those messages and
provides a report on objects which did not completely deallocate themselves,
usually because of a reference being held."
}

sub execute {
    my $self = shift;

#$DB::single = 1;
    my $file = $self->file;
    my $fh = IO::File->new($file,'r');

    unless ($fh) {
        $self->error_message("Can't open input file: $!");
        return;
    }

    # for a given state, it's legal predecessor
    my %prev_states = ( 'PRUNE object'       => '',
                        'DESTROY object'     => 'PRUNE object',
                        'UNLOAD object'      => 'DESTROY object',
                        'DELETE object'      => 'UNLOAD object',
                        'BURY object'        => 'DELETE object',
                        'DESTROY deletedref' => 'BURY object',
                      );
    my %next_states = reverse %prev_states;
    # After this we stop stracking it
    my %terminal_states = ( 'DESTROY deletedref' => 1 );
    my %objects;


    while(<$fh>) {
        chomp;

        my ($action,$refaddr);
        if (m/MEM ((PRUNE|DESTROY|UNLOAD|DELETE|BURY) (object|deletedref)) (\S+)/) {
            $action = $1;
            my $refstr = $4;
            ($refaddr) = ($refstr =~ m/=HASH\((.*)\)/);
        } else {
            next;
        }
        my($class,$id) = m/class (\S+) id (.*)/;   # These don't appear in the deletedref line, and are optional

        my $expected_prev_state = $prev_states{$action};
        if (defined $expected_prev_state && $expected_prev_state) {
            # This state must have a predecessor
            if ($objects{$expected_prev_state}->{$refaddr}) {
                if ($terminal_states{$action}) {
                    delete $objects{$expected_prev_state}->{$refaddr};
                } else {
                    $objects{$action}->{$refaddr} = delete $objects{$expected_prev_state}->{$refaddr};
                }
            } else {
                print STDERR "$action for $refaddr without matching $expected_prev_state at line $.\n";
            }

        } elsif (defined $expected_prev_state) {
            # The initial state
            $objects{$action}->{$refaddr} = $_;

        } else {
            print STDERR "Unknown action $action at line $.\n";
        }
    }

    foreach my $action (keys %objects) {
        if (keys %{$objects{$action}} ) {
            print "\n$action but not $next_states{$action}\n";
            foreach (keys %{$objects{$action}}) {
                print "$_ : ",$objects{$action}->{$_},"\n";
            }
        }
    }

    return 1;
}

1;