4 use File::Basename qw(&basename &dirname);
7 # List explicitly here the variables you want Configure to
8 # generate. Metaconfig only looks for shell variables, so you
9 # have to mention them as if they were shell variables, not
10 # %Config entries. Thus you write
12 # to ensure Configure will look for $Config{startperl}.
14 # This forces PL files to create target in same directory as PL file.
15 # This is so that make depend always knows where to find PL derivatives.
17 ($file = basename($0)) =~ s/\.PL$//i;
18 $file .= '.COM' if ($^O eq 'VMS');
20 my $dprof_pm = File::Spec->catfile(File::Spec->updir, 'ext', 'Devel', 'DProf', 'DProf.pm');
22 open( PM, "<$dprof_pm" ) || die "Can't open $dprof_pm: $!";
24 if( /^\$Devel::DProf::VERSION\s*=\s*'([\d._]+)'/ ){
31 die "Did not find VERSION in $dprof_pm";
34 foreach my $s (qw(/bin/stty /usr/bin/stty)) {
40 open OUT,">$file" or die "Can't create $file: $!";
42 print "Extracting $file (with variable substitutions)\n";
44 # In this section, perl variables will be expanded during extraction.
45 # You can use $Config{...} to use Configure variables.
47 print OUT <<"!GROK!THIS!";
49 eval 'exec perl -S \$0 "\$@"'
54 my \$VERSION = '$VERSION';
59 # In the following, perl variables are not expanded during extraction.
61 print OUT <<'!NO!SUBS!';
64 dprofpp - display perl profile data
68 dprofpp [B<-a>|B<-z>|B<-l>|B<-v>|B<-U>] [B<-d>] [B<-s>|B<-r>|B<-u>] [B<-q>] [B<-F>] [B<-I|-E>] [B<-O cnt>] [B<-A>] [B<-R>] [B<-S>] [B<-g subroutine>] [B<-G> <regexp> [B<-P>]] [B<-f> <regexp>] [profile]
70 dprofpp B<-T> [B<-F>] [B<-g subroutine>] [profile]
72 dprofpp B<-t> [B<-F>] [B<-g subroutine>] [profile]
74 dprofpp B<-G> <regexp> [B<-P>] [profile]
76 dprofpp B<-p script> [B<-Q>] [other opts]
78 dprofpp B<-V> [profile]
82 The I<dprofpp> command interprets profile data produced by a profiler, such
83 as the Devel::DProf profiler. Dprofpp will read the file F<tmon.out> and
84 display the 15 subroutines which are using the most time. By default
85 the times for each subroutine are given exclusive of the times of their
88 To profile a Perl script run the perl interpreter with the B<-d> switch. So
89 to profile script F<test.pl> with Devel::DProf use the following:
91 $ perl5 -d:DProf test.pl
93 Then run dprofpp to analyze the profile. The output of dprofpp depends
94 on the flags to the program and the version of Perl you're using.
97 Total Elapsed Time = 1.67 Seconds
98 User Time = 0.61 Seconds
100 %Time Seconds #Calls sec/call Name
101 52.4 0.320 2 0.1600 main::foo
102 45.9 0.280 200 0.0014 main::bar
103 0.00 0.000 1 0.0000 DynaLoader::import
104 0.00 0.000 1 0.0000 main::baz
106 The dprofpp tool can also run the profiler before analyzing the profile
107 data. The above two commands can be executed with one dprofpp command.
109 $ dprofpp -u -p test.pl
111 Consult L<Devel::DProf/"PROFILE FORMAT"> for a description of the raw profile.
121 Percentage of time spent in this routine.
125 Number of calls to this routine.
129 Average number of seconds per call to this routine.
137 Time (in seconds) spent in this routine and routines called from it.
141 Time (in seconds) spent in this routine (not including those called
146 Average time (in seconds) spent in each call of this routine
147 (including those called from it).
157 Sort alphabetically by subroutine names.
161 Reverse whatever sort is used
165 Count timing for autoloaded subroutine as timing for C<*::AUTOLOAD>.
166 Otherwise the time to autoload it is counted as time of the subroutine
167 itself (there is no way to separate autoload time from run time).
169 This is going to be irrelevant with newer Perls. They will inform
170 C<Devel::DProf> I<when> the C<AUTOLOAD> switches to actual subroutine,
171 so a separate statistics for C<AUTOLOAD> will be collected no matter
172 whether this option is set.
176 Count anonymous subroutines defined in the same package separately.
180 (default) Display all subroutine times exclusive of child subroutine times.
184 Force the generation of fake exit timestamps if dprofpp reports that the
185 profile is garbled. This is only useful if dprofpp determines that the
186 profile is garbled due to missing exit timestamps. You're on your own if
187 you do this. Consult the BUGS section.
191 Display all subroutine times inclusive of child subroutine times.
195 Sort by number of calls to the subroutines. This may help identify
196 candidates for inlining.
200 Show only I<cnt> subroutines. The default is 15.
204 Tells dprofpp that it should profile the given script and then interpret its
205 profile data. See B<-Q>.
209 Used with B<-p> to tell dprofpp to quit after profiling the script, without
210 interpreting the data.
214 Do not display column headers.
218 Display elapsed real times rather than user+system times.
222 Display system times rather than user+system times.
226 Display subroutine call tree to stdout. Subroutine statistics are
231 Display subroutine call tree to stdout. Subroutine statistics are not
232 displayed. When a function is called multiple consecutive times at the same
233 calling level then it is displayed once with a repeat count.
237 Display I<merged> subroutine call tree to stdout. Statistics are
238 displayed for each branch of the tree.
240 When a function is called multiple (I<not necessarily consecutive>)
241 times in the same branch then all these calls go into one branch of
242 the next level. A repeat count is output together with combined
243 inclusive, exclusive and kids time.
245 Branches are sorted with regard to inclusive time.
249 Do not sort. Display in the order found in the raw profile.
253 Display user times rather than user+system times.
257 Print dprofpp's version number and exit. If a raw profile is found then its
258 XS_VERSION variable will be displayed, too.
262 Sort by average time spent in subroutines during each call. This may help
263 identify candidates for inlining.
267 (default) Sort by amount of user+system time used. The first few lines
268 should show you which subroutines are using the most time.
270 =item B<-g> C<subroutine>
272 Ignore subroutines except C<subroutine> and whatever is called from it.
276 Aggregate "Group" all calls matching the pattern together.
277 For example this can be used to group all calls of a set of packages
279 -G "(package1::)|(package2::)|(package3::)"
281 or to group subroutines by name:
287 Used with -G to aggregate "Pull" together all calls that did not match -G.
291 Filter all calls matching the pattern.
295 Display brief help and exit.
299 Display long help and exit.
305 The environment variable B<DPROFPP_OPTS> can be set to a string containing
306 options for dprofpp. You might use this if you prefer B<-I> over B<-E> or
307 if you want B<-F> on all the time.
309 This was added fairly lazily, so there are some undesirable side effects.
310 Options on the commandline should override options in DPROFPP_OPTS--but
311 don't count on that in this version.
315 Applications which call _exit() or exec() from within a subroutine
316 will leave an incomplete profile. See the B<-F> option.
318 Any bugs in Devel::DProf, or any profiler generating the profile data, could
319 be visible here. See L<Devel::DProf/BUGS>.
321 Mail bug reports and feature requests to the perl5-porters mailing list at
322 F<E<lt>perl5-porters@perl.orgE<gt>>. Bug reports should include the
323 output of the B<-V> option.
327 dprofpp - profile processor
328 tmon.out - raw profile
332 L<perl>, L<Devel::DProf>, times(2)
338 dprofpp [options] [profile]
340 -A Count autoloaded to *AUTOLOAD.
341 -a Sort by alphabetic name of subroutines.
343 -E Sub times are reported exclusive of child times. (default)
344 -f Filter all calls matching the pattern.
345 -G Group all calls matching the pattern together.
346 -g subr Count only subs who are SUBR or called from SUBR.
347 -H Display long manual page.
348 -h Display this short usage message.
349 -I Sub times are reported inclusive of child times.
350 -l Sort by number of calls to subroutines.
351 -O cnt Specifies maximum number of subroutines to display.
352 -P Used with -G to pull all other calls together.
353 -p script Specifies name of script to be profiled.
354 -Q Used with -p to indicate that dprofpp should quit
355 after profiling the script, without interpreting the data.
356 -q Do not print column headers.
357 -R Count anonymous subs separately even if from the same package.
358 -r Use real elapsed time rather than user+system time.
359 -S Create statistics for all the depths.
360 -s Use system time rather than user+system time.
362 -t Show call tree, compressed.
363 -U Do not sort subroutines.
364 -u Use user time rather than user+system time.
365 -V Print dprofpp's version.
366 -v Sort by average amount of time spent in subroutines.
367 -z Sort by user+system time spent in subroutines. (default)
371 use Getopt::Std 'getopts';
372 use Config '%Config';
375 my $options = 'O:g:G:Pf:dlzaAvuTtqrRsUFEIp:QVShH';
377 $Monfile = 'tmon.out';
378 if( exists $ENV{DPROFPP_OPTS} ){
380 @ARGV = split( ' ', $ENV{DPROFPP_OPTS} );
383 # there was a filename.
389 getopts( $options ) or die "Try 'dprofpp -h' for help.\n";
391 # there was a filename, it overrides any earlier name.
395 if ( defined $opt_h ) {
399 if ( defined $opt_H ) {
401 Pod::Usage::pod2usage( {-verbose => 2, -input => $0 } );
405 if( defined $opt_V ){
407 print "$0 version: $VERSION\n";
408 open( $fh, "<$Monfile" ) && do {
409 local $XS_VERSION = 'early';
412 print "XS_VERSION: $XS_VERSION\n";
418 $sort = 'by_ctime' if defined $opt_I;
419 $sort = 'by_calls' if defined $opt_l;
420 $sort = 'by_alpha' if defined $opt_a;
421 $sort = 'by_avgcpu' if defined $opt_v;
426 $incl_excl = 'Exclusive';
427 $incl_excl = 'Inclusive' if defined $opt_I;
428 $whichtime = 'User+System';
429 $whichtime = 'System' if defined $opt_s;
430 $whichtime = 'Real' if defined $opt_r;
431 $whichtime = 'User' if defined $opt_u;
433 if( defined $opt_p ){
435 my $startperl = $Config{'startperl'};
437 $startperl =~ s/^#!//; # remove shebang
438 run_profiler( $opt_p, $prof, $startperl );
439 $Monfile = 'tmon.out'; # because that's where it is
440 exit(0) if defined $opt_Q;
442 elsif( defined $opt_Q ){
443 die "-Q is meaningful only when used with -p\n";
448 my $monout = $Monfile;
451 local $times = {}; # times in hz
452 local $ctimes = {}; # Cumulative times in hz
454 local $persecs = {}; # times in seconds
456 local $runtime; # runtime in seconds
459 local $rrun_utime = 0; # user time in hz
460 local $rrun_stime = 0; # system time in hz
461 local $rrun_rtime = 0; # elapsed run time in hz
462 local $rrun_ustime = 0; # user+system time in hz
464 local $deep_times = {count => 0 , kids => {}, incl_time => 0};
465 local $time_precision = 2;
468 open( $fh, "<$monout" ) || die "Unable to open $monout\n";
472 $rrun_ustime = $rrun_utime + $rrun_stime;
479 parsestack( $fh, $names, $calls, $times, $ctimes, $idkeys );
483 for(my $i = 0;$i < @$idkeys - 2;){
485 if($key =~ /$opt_f/){
486 splice(@$idkeys, $i, 1);
487 $runtime -= $$times{$key};
495 group($names, $calls, $times, $ctimes, $idkeys );
498 settime( \$runtime, $hz ) unless $opt_g;
500 exit(0) if $opt_T || $opt_t;
503 percalc( $calls, ($opt_I ? $ctimes : $times), $persecs, $idkeys );
506 @a = sort $sort @$idkeys;
512 display( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt, $a,
517 my ($names, $calls, $times, $ctimes, $idkeys ) = @_;
518 print "Option G Grouping: [$opt_G]\n";
519 # create entries to store grouping
520 $$names{$opt_G} = $opt_G;
523 $$ctimes{$opt_G} = 0;
524 $$idkeys[@$idkeys] = $opt_G;
525 # Sum calls for the grouping
529 $$names{$other} = $other;
532 $$ctimes{$other} = 0;
533 $$idkeys[@$idkeys] = $other;
536 for(my $i = 0;$i < @$idkeys - 2;){
538 if($key =~ /$opt_G/){
539 $$calls{$opt_G} += $$calls{$key};
540 $$times{$opt_G} += $$times{$key};
541 $$ctimes{$opt_G} += $$ctimes{$key};
542 splice(@$idkeys, $i, 1);
546 $$calls{$other} += $$calls{$key};
547 $$times{$other} += $$times{$key};
548 $$ctimes{$other} += $$ctimes{$key};
549 splice(@$idkeys, $i, 1);
555 print "Grouping [$opt_G] Calls: [$$calls{$opt_G}]\n".
556 "Grouping [$opt_G] Times: [$$times{$opt_G}]\n".
557 "Grouping [$opt_G] IncTimes: [$$ctimes{$opt_G}]\n";
560 # Sets $runtime to user, system, real, or user+system time. The
561 # result is given in seconds.
564 my( $runtime, $hz ) = @_;
569 $$runtime = ($rrun_rtime - $overhead)/$hz;
572 $$runtime = ($rrun_stime - $overhead)/$hz;
575 $$runtime = ($rrun_utime - $overhead)/$hz;
578 $$runtime = ($rrun_ustime - $overhead)/$hz;
580 $$runtime = 0 unless $$runtime > 0;
583 sub exclusives_in_tree {
584 my( $deep_times ) = @_;
587 # When summing, take into account non-rounded-up kids time.
588 for $kid (keys %{$deep_times->{kids}}) {
589 $kids_time += $deep_times->{kids}{$kid}{incl_time};
591 $kids_time = 0 unless $kids_time >= 0;
592 $deep_times->{excl_time} = $deep_times->{incl_time} - $kids_time;
593 $deep_times->{excl_time} = 0 unless $deep_times->{excl_time} >= 0;
594 for $kid (keys %{$deep_times->{kids}}) {
595 exclusives_in_tree($deep_times->{kids}{$kid});
597 $deep_times->{incl_time} = 0 unless $deep_times->{incl_time} >= 0;
598 $deep_times->{kids_time} = $kids_time;
601 sub kids_by_incl { $kids{$b}{incl_time} <=> $kids{$a}{excl_time}
605 my( $deep_times, $name, $level ) = @_;
606 exclusives_in_tree($deep_times);
611 if (%{$deep_times->{kids}}) {
612 $time = sprintf '%.*fs = (%.*f + %.*f)',
613 $time_precision, $deep_times->{incl_time}/$hz,
614 $time_precision, $deep_times->{excl_time}/$hz,
615 $time_precision, $deep_times->{kids_time}/$hz;
617 $time = sprintf '%.*f', $time_precision, $deep_times->{incl_time}/$hz;
619 print ' ' x (2*$level), "$name x $deep_times->{count} \t${time}s\n"
620 if $deep_times->{count};
622 for $kid (sort kids_by_incl %{$deep_times->{kids}}) {
623 display_tree( $deep_times->{kids}{$kid}, $kid, $level + 1 );
627 # Report the times in seconds.
629 my( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt,
630 $idkeys, $deep_times ) = @_;
631 my( $x, $key, $s, $cs );
632 #format: $ncalls, $name, $secs, $percall, $pcnt
635 display_tree( $deep_times, 'toplevel', -1 )
637 for( $x = 0; $x < @$idkeys; ++$x ){
638 $key = $idkeys->[$x];
639 $ncalls = $calls->{$key};
640 $name = $names->{$key};
641 $s = $times->{$key}/$hz;
642 $secs = sprintf("%.3f", $s );
643 $cs = $ctimes->{$key}/$hz;
644 $csecs = sprintf("%.3f", $cs );
645 $percall = sprintf("%.4f", $s/$ncalls );
646 $cpercall = sprintf("%.4f", $cs/$ncalls );
647 $pcnt = sprintf("%.2f",
648 $runtime? ((($opt_I ? $csecs : $secs) / $runtime) * 100.0): 0 );
650 $pcnt = $secs = $ncalls = $percall = "";
651 write while( length $name );
658 my ($source, $dest) = @_;
660 for my $kid_name (keys %$source) {
661 my $source_kid = delete $source->{$kid_name};
663 if (my $dest_kid = $dest->{$kid_name}) {
664 $dest_kid->{count} += $source_kid->{count};
665 $dest_kid->{incl_time} += $source_kid->{incl_time};
666 move_keys($source_kid->{kids},$dest_kid->{kids});
668 $dest->{$kid_name} = $source_kid;
674 my ($curdeep_times, $name, $t) = @_;
675 if ($name ne $curdeep_times->[-1]{name} and $opt_A) {
676 $name = $curdeep_times->[-1]{name};
678 die "Shorted?!" unless @$curdeep_times >= 2;
679 my $entry = $curdeep_times->[-2]{kids}{$name} ||= {
684 # Now transfer to the new node (could not do earlier, since name can change)
686 $entry->{incl_time} += $t - $curdeep_times->[-1]{enter_stamp};
688 move_keys($curdeep_times->[-1]->{kids},$entry->{kids});
694 my( $fh, $names, $calls, $times, $ctimes, $idkeys ) = @_;
696 my( $t, $syst, $realt, $usert );
697 my( $x, $z, $c, $id, $pack );
704 # remember last call depth and function name
711 my $in_level = not defined $opt_g; # Level deep in report grouping
712 my $curdeep_times = [$deep_times];
715 if ( $opt_u ) { $over_per_call = $over_utime }
716 elsif( $opt_s ) { $over_per_call = $over_stime }
717 elsif( $opt_r ) { $over_per_call = $over_rtime }
718 else { $over_per_call = $over_utime + $over_stime }
719 $over_per_call /= 2*$over_tests; # distribute over entry and exit
727 ($dir, $id, $pack, $name) = split;
728 if ($opt_R and ($name =~ /(?:::)?(__ANON__|END)$/)) {
731 $cv_hash{$id} = "$pack\::$name";
734 ($dir, $usert, $syst, $realt, $name) = split;
738 $syst = $stack[-1][0] if scalar @stack;
741 #warn("Inserted exit for $stack[-1][0].\n")
743 if (defined $realt) { # '+ times nam' '- times nam' or '@ incr'
744 if ( $opt_u ) { $t = $usert }
745 elsif( $opt_s ) { $t = $syst }
746 elsif( $opt_r ) { $t = $realt }
747 else { $t = $usert + $syst }
748 $t += $ot, next if $dir eq '@'; # Increments there
750 # "- id" or "- & name"
751 $name = defined $syst ? $syst : $cv_hash{$usert};
754 next unless $in_level or $name eq $opt_g;
755 if ( $dir eq '-' or $dir eq '*' ) {
756 my $ename = $dir eq '*' ? $stack[-1][0] : $name;
757 $overhead += $over_per_call;
758 if ($name eq "Devel::DProf::write") {
759 $overhead += $t - $dprof_stamp;
761 } elsif (defined $opt_g and $ename eq $opt_g) {
764 add_to_tree($curdeep_times, $ename,
765 $t - $overhead) if $opt_S;
766 exitstamp( \@stack, \@tstack,
768 $times, $ctimes, $name, \$in, $tab,
769 $curdeep_times, \%outer );
771 next unless $in_level or $name eq $opt_g;
772 if( $dir eq '+' or $dir eq '*' ){
773 if ($name eq "Devel::DProf::write") {
776 } elsif (defined $opt_g and $name eq $opt_g) {
779 $overhead += $over_per_call;
781 print ' ' x $in, "$name\n";
785 # suppress output on same function if the
786 # same calling level is called.
787 if ($l_in == $in and $l_name eq $name) {
790 $repstr = ' ('.++$repcnt.'x)'
792 print ' ' x $l_in, "$l_name$repstr\n"
801 if( ! defined $names->{$name} ){
802 $names->{$name} = $name;
804 $ctimes->{$name} = 0;
805 push( @$idkeys, $name );
809 push @$curdeep_times, { kids => {},
811 enter_stamp => $t - $overhead,
813 $x = [ $name, $t - $overhead ];
816 # my children will put their time here
818 } elsif ($dir ne '-'){
819 die "Bad profile: $_";
823 $repstr = ' ('.++$repcnt.'x)' if $repcnt;
824 print ' ' x $l_in, "$l_name$repstr\n";
827 while (my ($key, $count) = each %outer) {
829 warn "$key has $count unstacked calls in outer\n";
834 warn "Garbled profile is missing some exit time stamps:\n";
835 foreach $x (@stack) {
838 die "Try rerunning dprofpp with -F.\n";
839 # I don't want -F to be default behavior--yet
843 warn( "Faking " . scalar( @stack ) . " exit timestamp(s).\n");
844 foreach $x ( reverse @stack ){
846 exitstamp( \@stack, \@tstack,
847 $t - $overhead, $times,
848 $ctimes, $name, \$in, $tab,
849 $curdeep_times, \%outer );
850 add_to_tree($curdeep_times, $name,
856 if (defined $opt_g) {
857 $runtime = $ctimes->{$opt_g}/$hz;
858 $runtime = 0 unless $runtime > 0;
863 my($stack, $tstack, $t, $times, $ctimes, $name, $in, $tab, $deep, $outer) = @_;
868 die "Garbled profile, missing an enter time stamp";
870 if( $x->[0] ne $name and $opt_G and ($name =~ /$opt_G/)){
871 if ($x->[0] =~ /(?:::)?AUTOLOAD$/) {
876 warn( "Garbled profile, faking exit timestamp:\n\t$name => $x->[0].\n");
879 foreach $z (@stack, $x) {
882 die "Garbled profile, unexpected exit time stamp";
885 if( $opt_T || $opt_t ){
889 $c = pop( @$tstack );
890 # total time this func has been active
892 $ctimes->{$name} += $z
893 unless --$outer->{$name};
894 $times->{$name} += $z - $c;
895 # pass my time to my parent
897 $c = pop( @$tstack );
898 push( @$tstack, $c + $z );
906 if( ! /^#fOrTyTwO$/ ){
907 die "Not a perl profile";
914 $over_tests = 1 unless $over_tests;
915 $time_precision = length int ($hz - 1); # log ;-)
919 # Report avg time-per-function in seconds
921 my( $calls, $times, $persecs, $idkeys ) = @_;
922 my( $x, $t, $n, $key );
924 for( $x = 0; $x < @$idkeys; ++$x ){
925 $key = $idkeys->[$x];
927 $t = $times->{$key} / $hz;
928 $persecs->{$key} = $t ? $t / $n : 0;
933 # Runs the given script with the given profiler and the given perl.
936 my $profiler = shift;
937 my $startperl = shift;
938 my @script_parts = split /\s+/, $script;
940 system $startperl, "-d:$profiler", @script_parts;
942 my $cmd = join ' ', @script_parts;
943 die "Failed: $startperl -d:$profiler $cmd: $!";
948 sub by_time { $times->{$b} <=> $times->{$a} }
949 sub by_ctime { $ctimes->{$b} <=> $ctimes->{$a} }
950 sub by_calls { $calls->{$b} <=> $calls->{$a} }
951 sub by_alpha { $names->{$a} cmp $names->{$b} }
952 sub by_avgcpu { $persecs->{$b} <=> $persecs->{$a} }
954 sub rby_time { $times->{$a} <=> $times->{$b} }
955 sub rby_ctime { $ctimes->{$a} <=> $ctimes->{$b} }
956 sub rby_calls { $calls->{$a} <=> $calls->{$b} }
957 sub rby_alpha { $names->{$b} cmp $names->{$a} }
958 sub rby_avgcpu { $persecs->{$a} <=> $persecs->{$b} }
962 Total Elapsed Time = @>>>>>>> Seconds
963 (($rrun_rtime - $overhead) / $hz)
964 @>>>>>>>>>> Time = @>>>>>>> Seconds
968 %Time ExclSec CumulS #Calls sec/call Csec/c Name
972 my $fmt = ' ^>>> ^>>>> ^>>>>> ^>>>>> ^>>>>> ^>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
973 if (-t STDOUT and defined $stty and my ($cols) = `$stty -a` =~ /\bcolumns\s+(\d+)/)
975 $fmt .= '<' x ($cols - length $fmt) if $cols > 80;
978 eval "format STAT = \n$fmt" . '
979 $pcnt, $secs, $csecs, $ncalls, $percall, $cpercall, $name
984 close OUT or die "Can't close $file: $!";
985 chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
986 exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';