Whamcloud - gitweb
Merge b_md into HEAD
[fs/lustre-release.git] / lustre / utils / llparser.pm
1 #!/usr/bin/perl
2 # Copyright (C) 2002 Cluster File Systems, Inc.
3 # Author: Hariharan Thantry <thantry@users.sourceforge.net>
4
5 #   This file is part of Lustre, http://www.lustre.org.
6 #
7 #   Lustre is free software; you can redistribute it and/or
8 #   modify it under the terms of version 2 of the GNU General Public
9 #   License as published by the Free Software Foundation.
10 #
11 #   Lustre is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with Lustre; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #
20
21
22 package llparser;
23 require Exporter;
24 @ISA = qw(Exporter);
25 @EXPORT = qw(parse_file print_rpcrelations parse_foptions %ll_subsystems 
26         %subsysnum %trace_masks $e_subsys $e_mask $e_processor $e_time 
27         $e_file $e_line $e_function $e_pid $e_stack $e_fmtstr $e_backref 
28         $e_treeparent $e_numchildren $e_youngestchild $e_next $e_pidhead 
29         $e_rpcsndrcv $e_rpcpid $e_rpcxid $e_rpcnid $e_rpcopc $e_rpcnext 
30         $e_curlineref $SEND $RCV);
31
32 ($e_subsys, 
33  $e_mask, 
34  $e_processor, 
35  $e_time, 
36  $e_file, 
37  $e_line, 
38  $e_function, 
39  $e_pid, 
40  $e_stack, 
41  $e_fmtstr, 
42  $e_treeparent, 
43  $e_numchildren,
44  $e_youngestchild, 
45  $e_pidhead,
46  $e_next, 
47  $e_backref) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
48
49 ($e_rpcpid,
50  $e_rpcxid,
51  $e_rpcnid,
52  $e_rpcopc,
53  $e_rpcnext, 
54  $e_rpcsndrcv,
55  $e_curlineref) = (0, 1, 2, 3, 4, 5, 6); 
56
57 $SEND = 0;
58 $RCV  = 1;
59
60 $REGEX=qr/^\s*(\w+)\s*:\s*(\d+)\s*:\s*(\d+)\s*:\s*(\d+\.(?:\d+))\s*\(\s*([^:]+)\s*:\s*(\d+)\s*:\s*([^()]+)\s*\(\)\s*(?:(?:\d+)\s*\|\s*)?(\d+)\s*\+\s*(\d+)\s*(?:.*)\):(.*)$/;
61
62 $RPCREGEX = qr/^\s*(?:Sending|Handling)\s*RPC\s*pid:xid:nid:opc\s*(\d+):(?:0x)?(\w+):(?:0x)?(\w+):(\d+)\s*$/;
63 $FILEOPTIONREGEX = qr/(--server)|(-s)/;
64 $SENDING = qr/Sending/;
65
66
67 # Needs to match definition in portals/include/linux/kp30.h
68 %ll_subsystems = ("00" => "UNDEFINED", "01" => "MDC", "02" => "MDS", 
69                   "03" => "OSC",  "04" => "OST",  "05" => "CLASS",
70                   "06" => "OBDFS","07" => "LLITE","08" => "RPC",
71                   "09" => "EXT2OBD","0a" => "PORTALS","0b" => "SOCKNAL",
72                   "0c" => "QSWNAL","0d" => "PINGER","0e" => "FILTER",
73                   "0f" => "TRACE","10" => "ECHO","11" => "LDLM",
74                   "12" => "LOV", "13" => "GMNAL","14" => "PTLROUTER" );
75
76 %subsysnum;
77 $subsysnum->{UNDEFINED} = 0;
78 $subsysnum->{MDC} = 1;
79 $subsysnum->{MDS} = 2;
80 $subsysnum->{OSC} = 3;
81 $subsysnum->{OST} = 4;
82 $subsysnum->{CLASS} = 5;
83 $subsysnum->{OBDFS} = 6;
84 $subsysnum->{LLITE} = 7;
85 $subsysnum->{RPC} = 8;
86 $subsysnum->{EXT2OBD} = 9;
87 $subsysnum->{PORTALS} = 10;
88 $subsysnum->{SOCKNAL} = 11;
89 $subsysnum->{QSWNAL} = 12;
90 $subsysnum->{PINGER} = 13;
91 $subsysnum->{FILTER} = 14;
92 $subsysnum->{TRACE} = 15; # obdtrace, not to be confused with D_TRACE */
93 $subsysnum->{ECHO} = 16;
94 $subsysnum->{LDLM} = 17;
95 $subsysnum->{LOV} = 18;
96 $subsysnum->{GMNAL} = 19;
97 $subsysnum->{PTLROUTER} = 20;
98
99 %tracemasks;
100 $tracemasks->{TRACE} = 1 << 0; # /* ENTRY/EXIT markers */
101 $tracemasks->{INODE} = 1 << 1; #
102 $tracemasks->{SUPER} = 1 << 2; #
103 $tracemasks->{EXT2} = 1 << 3; # /* anything from ext2_debug */
104 $tracemasks->{MALLOC} = 1 << 4; # /* print malloc, free information */
105 $tracemasks->{CACHE} = 1 << 5; # /* cache-related items */
106 $tracemasks->{INFO} = 1 << 6; # /* general information */
107 $tracemasks->{IOCTL} = 1 << 7; # /* ioctl related information */
108 $tracemasks->{BLOCKS} = 1 << 8; # /* ext2 block allocation */
109 $tracemasks->{NET} = 1 << 9; # /* network communications */
110 $tracemasks->{WARNING} = 1 << 10; #
111 $tracemasks->{BUFFS} = 1 << 11; #
112 $tracemasks->{OTHER} = 1 << 12; #
113 $tracemasks->{DENTRY} = 1 << 13; #
114 $tracemasks->{PORTALS} = 1 << 14; # /* ENTRY/EXIT markers */
115 $tracemasks->{PAGE} = 1 << 15; # /* bulk page handling */
116 $tracemasks->{DLMTRACE} = 1 << 16; #
117 $tracemasks->{ERROR} = 1 << 17; # /* CERROR} = ...) == CDEBUG} = D_ERROR, ...) */
118 $tracemasks->{EMERG} = 1 << 18; # /* CEMERG} = ...) == CDEBUG} = D_EMERG, ...) */
119 $tracemasks->{HA} = 1 << 19; # /* recovery and failover */
120 $tracemasks->{RPCTRACE} = 1 << 19; # /* recovery and failover */
121
122 # Contains all the file names, the first filename is the 
123 # client. After that are all servers.
124 my @filearray = ();
125
126
127 # Create backlinks between array entries based on the calling sequence
128 # For each new PID encountered, the first entry will be present in the 
129 # PID hash.
130
131 sub create_links {
132     my $arrayref = shift @_;
133     my $pidhashref = shift @_;
134     my $stitchref = shift @_;
135     my %local_hash;
136     my $hash_lineref;
137     my $tmpfmtref;
138     my $tmpref;
139     my $firstlineaftermarker = 0;
140
141     foreach $lineref (@$arrayref) {
142         next if ($lineref->[$e_time] == 0); # Skip the client marker line
143         my $pidprevious = $pidhashref->{$lineref->[$e_pid]};
144         if ($pidprevious->[$e_next] == 0) {
145             $pidprevious->[$e_next] = $lineref;
146             if (exists $local_hash{$lineref->[$e_pid]} 
147                 && $firstlineaftermarker) {
148                 $hash_lineref=$local_hash{$lineref->[$e_pid]};
149                 $hash_lineref->[$e_next] =$lineref;
150                 $firstlineaftermarker = 0;
151             } 
152         } elsif ($local_hash{$lineref->[$e_pid]} == 0) {
153                 # True only for the first line, the marker line.
154                 $local_hash{$lineref->[$e_pid]}=$lineref;
155                 #print "LINE ADDED TO HASH: @$lineref\n";
156                 $firstlineaftermarker = 1; 
157         }
158         # Stack grows upward (assumes x86 kernel)
159         if ($lineref->[$e_stack] < $pidprevious->[$e_stack]) {
160             # lineref is not a child of pidprevious, find its parent
161           LINE: while(($lineref->[$e_stack] < $pidprevious->[$e_stack]) &&
162                       ($lineref->[$e_function] == $pidprevious->[$e_function])
163                       ) {
164                           #This second part of the comparision is a HACK  
165                           last LINE if ($pidprevious->[$e_backref] == 0); 
166                           $pidprevious = $pidprevious->[$e_backref];
167           }
168         }
169         if ($lineref->[$e_stack] > $pidprevious->[$e_stack]) {
170             # lineref is child of pidprevious, with the caveat that they must
171             # belong to different functions. This is a HACK 
172             # until CDEBUG is modified
173             while($lineref->[$e_function] eq $pidprevious->[$e_function]) {
174               last if ($pidprevious->[$e_backref] == 0);
175               $pidprevious = $pidprevious->[$e_backref];
176             }   
177
178             $lineref->[$e_backref] = $pidprevious;
179             $pidprevious->[$e_numchildren]++;
180         } else {
181             # lineref is sibling of pidprevious
182             $lineref->[$e_numchildren] = 0;
183             $lineref->[$e_backref] = $pidprevious->[$e_backref];
184             ($lineref->[$e_backref])->[$e_numchildren]++;
185         }
186
187         $pidhashref->{$lineref->[$e_pid]} = $lineref;
188         $lineref->[$e_youngestchild] = $lineref;
189         while ($pidprevious->[$e_backref] != 0) {
190             $pidprevious->[$e_youngestchild] = $lineref;
191             $pidprevious = $pidprevious->[$e_backref];
192         }
193         $pidprevious->[$e_youngestchild] = $lineref;
194         $lineref->[$e_pidhead]=$pidprevious;
195         
196         # Stitch together rpc's
197         if($lineref->[$e_fmtstr] =~ $RPCREGEX) {
198             #print "RPC LINE: @$lineref\n";
199             $tmpfmtref = [$1, $2, $3, $4, 0, 0, 0];
200             if ($lineref->[$e_fmtstr] =~ $SENDING) {
201                 $tmpfmtref->[$e_rpcsndrcv] = $SEND;
202             } else { $tmpfmtref->[$e_rpcsndrcv] = $RCV; }
203             $tmpfmtref->[$e_curlineref] = $lineref;
204             $stitchref->{$lineref->[$e_time]} = $tmpfmtref;
205             
206         }
207             
208     }
209 match_rpcs($stitchref);
210 return $arrayref;       
211 }
212
213
214
215
216 # Main loop, parses the debug log
217
218 sub parse_file {
219     my %hasharray;
220     my $input_files = shift;
221     
222     my $stitch_ref = shift;
223     my $pid = shift;
224     my $rpctrace = shift;
225     my $trace = shift;
226     my $nodlm = shift;
227     my $noclass = shift;
228     my $nonet = shift;
229
230     print "$pid, $rpctrace, $nodlm, $noclass, $nonet\n";
231     $backref = 0;
232     $treeparent = 0;
233     $numchildren = 0;
234     $youngestchild = 0;
235     $next = 0;
236     $pidhead = 0;
237     $iter = 0;
238                         
239     foreach $file (@$input_files) {
240         
241         open(FILEHANDLE, $file) or die "Can't open file: $file\n";
242         while(<FILEHANDLE>) {
243             if (/$REGEX/) {
244                 @parsed_line=($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 
245                               $treeparent, $numchildren, $youngestchild, 
246                               $pidhead, $next, $backref);
247                 next if (($parsed_line[$e_pid] != $pid) && 
248                          ($pid) && ($iter == 0));
249                 next if (($parsed_line[$e_mask] != $tracemasks->{RPCTRACE}) 
250                          && ($rpctrace));
251                 next if ($trace && $parsed_line[$e_mask] != 
252                          $tracemasks->{TRACE});
253                 next if ($nodlm && hex($parsed_line[$e_subsys]) == 
254                          $subsysnum->{LDLM});
255                 next if ($noclass && hex($parsed_line[$e_subsys]) == 
256                          $subsysnum->{CLASS});
257                 next if ($nonet && (hex($parsed_line[$e_subsys]) == 
258                                     $subsysnum->{RPC} ||
259                                     hex($parsed_line[$e_subsys]) == 
260                                     $subsysnum->{NET} ||        
261                                     hex($parsed_line[$e_subsys]) == 
262                                     $subsysnum->{PORTALS} ||
263                                     hex($parsed_line[$e_subsys]) == 
264                                     $subsysnum->{SOCKNAL} ||
265                                     hex($parsed_line[$e_subsys]) == 
266                                     $subsysnum->{QSWNAL} ||
267                                     hex($parsed_line[$e_subsys]) == 
268                                     $subsysnum->{GMNAL}));      
269                 
270                 
271                 if (!exists($hasharray{$parsed_line[$e_pid]})) {
272                     # Push a marker for the beginning of this PID
273                     my @marker_line;
274                     $marker_line[$e_subsys] = 0;
275                     $marker_line[$e_mask] = 0;
276                     $marker_line[$e_processor] = 0;
277                     $marker_line[$e_time] = $parsed_line[$e_time];
278                     $marker_line[$e_file] = 0;
279                     $marker_line[$e_line] = 0;
280                     $marker_line[$e_function] = 0;
281                     $marker_line[$e_pid] = $parsed_line[$e_pid];
282                    # marker lines are everyone's parent, so stack value zero
283                     $marker_line[$e_stack] = 0; 
284                     $marker_line[$e_fmtstr] = "";
285                     $marker_line[$e_treeparent] = 0;
286                     $marker_line[$e_numchildren] = 0;
287                     $marker_line[$e_youngestchild] = 0;
288                     $marker_line[$e_pidhead] = 0;
289                     $marker_line[$e_next]= \@parsed_line;
290                     $marker_line[$e_backref] = 0;
291                     $hasharray{$parsed_line[$e_pid]} = \@marker_line;
292                     push @$array_parsed, [ @marker_line ];
293                     
294                 }
295                 push @$array_parsed, [ @parsed_line ];
296             }
297             
298         }
299         close(FILEHANDLE);
300         if ($iter == 0) {
301             # Insert end of client line marker, an all zero pattern;
302             @marker_line = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
303             push @$array_parsed, [ @marker_line ]; 
304             
305         }
306         $iter ++;
307     }
308     
309     $array_parsed=create_links($array_parsed, \%hasharray, $stitch_ref);
310     #print_array($array_parsed);
311     return $array_parsed;
312 }
313
314 sub print_array {
315
316     my $arrayref = shift;
317     foreach $lineref(@$arrayref){
318         if ($lineref->[$e_backref]==0){
319                 print "MARKER LINE(addr): $lineref contents: [@$lineref]\n";
320         } else {
321
322                 print "REGULAR LINE (addr) :$lineref contents:[@$lineref]\n";
323         }
324     }
325     
326 }
327
328 sub print_rpcrelations {
329
330     my $rpchashref = shift;
331     foreach $rpckeys (sort keys %$rpchashref) {
332         $tmpref = $rpchashref->{$rpckeys};
333         #print "Key: $rpckeys, Contents: @$tmpref\n";
334
335     }
336
337 }
338 sub match_rpcs {
339     my $rpchashref = shift;
340     foreach $rpckeys (sort keys %$rpchashref) {
341         $tmpref = $rpchashref->{$rpckeys};
342         #print "MATCHING: $@tmpref...\n";
343         foreach $cmpkeys (sort keys %$rpchashref) {
344             next if($cmpkeys == $rpckeys);
345             $cmpref = $rpchashref->{$cmpkeys};
346          #   print "Line compared: @$cmpref\n";
347             next if ($tmpref->[$e_rpcsndrcv] == $cmpref->[$e_rpcsndrcv]);
348             next if ($tmpref->[$e_rpcpid] != $cmpref->[$e_rpcpid]);
349             next if ($tmpref->[$e_rpcxid] != $cmpref->[$e_rpcxid]);
350             if ($tmpref->[$e_rpcsndrcv] == $SEND) {
351                 $tmpref->[$e_rpcnext] = $cmpkeys;
352                 #print "MACTHED: KEY 1: $rpckeys CONTENTS: @$tmpref", 
353                 #"KEY2: $cmpkeys CONTENTS: @$cmpref\n"
354                 
355             }
356                     
357         }
358
359     }
360
361 }
362
363 sub getnextchild {
364     my $rootline = shift;
365     my $lineref = shift;
366     my $tempref = $lineref->[$e_next];
367     if ($tempref == 0)  {
368         return 0;
369     }
370
371     if (($tempref->[$e_stack] > $rootline->[$e_stack]) ||
372         (($tempref->[$e_stack] <= $rootline->[$e_stack]) &&
373          ($tempref->[$e_function] == $rootline->[$e_function])
374          )){
375         # Child
376         return $tempref;
377         
378     }
379         return 0;
380         
381         
382 }
383
384
385 sub parse_foptions {
386     
387     my $inarg = shift;
388     my $idx = 0;
389     foreach $elem(@$inarg) {
390         next if ($elem =~ /$FILEOPTIONREGEX/);
391         $filearray[$idx] = $elem;
392         $idx++;    
393     }
394     return \@filearray;
395 }
396
397 1;
398 #$array_parsed=parse_file();
399 #print_array($array_parsed);