File Coverage

blib/lib/Devel/MAT/Tool/Object/Pad.pm
Criterion Covered Total %
statement 64 66 96.9
branch 30 36 83.3
condition n/a
subroutine 10 10 100.0
pod 2 3 66.6
total 106 115 92.1


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2022 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Tool::Object::Pad 0.01;
7              
8 3     3   19511110 use v5.14;
  3         10  
9 3     3   14 use warnings;
  3         3  
  3         91  
10 3     3   13 use base qw( Devel::MAT::Tool );
  3         3  
  3         606  
11              
12 3     3   4240 use Syntax::Keyword::Match;
  3         6  
  3         25  
13              
14 3     3   146 use List::Util qw( first );
  3         6  
  3         1990  
15              
16             =encoding UTF-8
17              
18             =head1 NAME
19              
20             C - extend C to analyse C-based programs
21              
22             =head1 DESCRIPTION
23              
24             This third-party C tool adds commands and other support to help
25             with analysis of programs that use L. Once installed, it is
26             loaded automatically by the F shell whenever a dumpfile that uses
27             C is loaded.
28              
29             =head1 COMMANDS
30              
31             =head2 classes
32              
33             Prints a list of the C classes and roles.
34              
35             pmat> classes
36             role ARole at C_STRUCT(Object::Pad/ClassMeta.role) at 0x55d7c17a1550
37             class HashClass at C_STRUCT(Object::Pad/ClassMeta.class) at 0x55d7c1776b70
38             ...
39              
40             =head2 fields
41              
42             Prints the values of all the fields of a given instance of an
43             C-based class.
44              
45             pmat> fields 0x55d7c173d4b8
46             The field AV ARRAY(3)=NativeClass at 0x55d7c173d4b8
47             Ix Field Value
48             0 $sfield SCALAR(UV) at 0x55d7c173d938 = 123
49             ...
50              
51             =head1 EXTENSIONS TO COMMANDS
52              
53             =head2 outrefs, identify, ...
54              
55             Outbound references from ARRAY SVs that are the backing fields of object
56             instances will print elements using field names, instead of plain indexes.
57              
58             pmat> outrefs 0x55d7c173d4b8
59             s the $sfield field SCALAR(UV) at 0x55d7c173d938
60             ...
61              
62             pmat> identify 0x55d7c17606d8
63             REF() at 0x55d7c17606d8 is:
64             └─the %hfield field of ARRAY(3)=NativeClass at 0x55d7c173d4b8, which is:
65             ...
66              
67             =cut
68              
69             sub AUTOLOAD_TOOL
70             {
71 2     2 1 88927 shift;
72 2         5 my ( $pmat ) = @_;
73              
74 2 50       4 return 1 if eval { $pmat->find_symbol( '%Object::Pad::' ) };
  2         7  
75             }
76              
77             sub init_tool
78             {
79 2     2 1 352 my $self = shift;
80              
81 2         13 require Devel::MAT::Tool::Object::Pad::_SVs;
82 2         6 require Devel::MAT::Tool::Object::Pad::_Commands;
83              
84 2         10 my $df = $self->df;
85 2         16 my $pmat = $self->pmat;
86              
87 2         10 $self->{classes_by_name} = \my %classes_by_name;
88              
89 2         17 $self->{vtbl_ptr} = $df->root_at( "the Object::Pad backing AV VTBL" );
90              
91 2         22 my $heap_total = scalar $df->heap;
92 2         12 my $count;
93              
94             # Find all the classes
95 2         5 $count = 0;
96 2         4 foreach my $sv ( $df->heap ) {
97 148681         550002 $count++;
98 148681 100       178316 $self->report_progress( sprintf "Finding Object::Pad structures in %d of %d (%.2f%%)",
99             $count, $heap_total, 100*$count / $heap_total ) if ($count % 10000) == 0;
100              
101 148681 100       234117 next unless $sv->type eq "C_STRUCT";
102              
103             match( $sv->structtype->name : eq ) {
104             case( "Object::Pad/ClassMeta.class" ) {
105 5         109 bless $sv, "Devel::MAT::Tool::Object::Pad::_ClassSV";
106              
107 5         23 $classes_by_name{ $sv->objectpad_name } = $sv;
108              
109 5         91 $pmat->find_symbol( "%" . $sv->objectpad_name . "::" )->{objectpad_class_at} = $sv->addr;
110             }
111             case( "Object::Pad/ClassMeta.role" ) {
112 1         22 bless $sv, "Devel::MAT::Tool::Object::Pad::_RoleSV";
113              
114 1         5 $classes_by_name{ $sv->objectpad_name } = $sv;
115              
116 1         18 $pmat->find_symbol( "%" . $sv->objectpad_name . "::" )->{objectpad_class_at} = $sv->addr;
117             }
118             case( "Object::Pad/FieldMeta" ) {
119 8         185 bless $sv, "Devel::MAT::Tool::Object::Pad::_FieldSV";
120             }
121             case( "Object::Pad/MethodMeta" ) {
122 1         50 bless $sv, "Devel::MAT::Tool::Object::Pad::_MethodSV";
123             }
124 17 100       44 case( "Object::Pad/RoleEmbedding" ) {
    100          
    100          
    100          
    100          
125 1         20 bless $sv, "Devel::MAT::Tool::Object::Pad::_RoleEmbeddingSV";
126             }
127             }
128             }
129              
130             # Find all the instances of them
131 2         9501 $count = 0;
132 2         20 foreach my $sv ( $df->heap ) {
133 148681         973913 $count++;
134 148681 100       178334 $self->report_progress( sprintf "Finding Object::Pad instances in %d of %d (%.2f%%)",
135             $count, $heap_total, 100*$count / $heap_total ) if ($count % 10000) == 0;
136              
137 148681 100       214943 my $package = $sv->blessed or next;
138 669 100       7655 my $class = $classes_by_name{ $package->stashname } or next;
139              
140 5         42 my $fieldav;
141              
142             match( $class->objectpad_repr : == ) {
143             case(0) { # REPR_NATIVE
144 3         4 $fieldav = $sv;
145             }
146             case(1) { # REPR_HASH
147             # TODO: Signal this as some sort of error condition
148 1 50       8 next unless $sv->type eq "HASH";
149              
150 1         4 $fieldav = $sv->value( "Object::Pad/slots" )->rv;
151             }
152             case(2) { # REPR_MAGIC
153 1 50   1   10 my $fieldmagic = first { $_->type eq "~" and $_->vtbl == $self->{vtbl_ptr} } $sv->magic or
  1 50       64  
154             next;
155              
156 1         48 $fieldav = $fieldmagic->obj;
157             }
158 5 100       19 default {
    100          
    50          
159 0         0 warn "TODO: Find fields AV for repr=" . $class->objectpad_repr;
160 0         0 next;
161             }
162             }
163              
164 5         63 $sv->{objectpad_fields_at} = $fieldav->addr;
165              
166 5         48 bless $fieldav, "Devel::MAT::Tool::Object::Pad::_FieldAV";
167 5         49 $fieldav->{objectpad_instance_at} = $sv->addr;
168             }
169             }
170              
171             sub classes_by_name
172             {
173 1     1 0 2 my $self = shift;
174 1         6 return $self->{classes_by_name};
175             }
176              
177             =head1 SV METHODS
178              
179             This tool adds the following SV methods.
180              
181             =cut
182              
183             =head2 objectpad_class (STASH)
184              
185             $class = $stash->objectpad_class
186              
187             If the stash is the package for an C-based class, returns the
188             C containing information about the class itself. Otherwise, returns
189             C.
190              
191             =cut
192              
193             # Extension to STASHes
194             sub Devel::MAT::SV::STASH::objectpad_class
195             {
196 6     6   8 my $self = shift;
197 6 50       14 return undef unless my $class_at = $self->{objectpad_class_at};
198 6         13 return $self->df->sv_at( $class_at );
199             }
200              
201             =head1 AUTHOR
202              
203             Paul Evans
204              
205             =cut
206              
207             0x55AA;