2 # Copyright (C) 2002 Cluster File Systems, Inc.
3 # Author: Hariharan Thantry <thantry@users.sourceforge.net>
5 # This file is part of Lustre, http://www.lustre.org.
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.
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.
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.
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);
47 $e_backref) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
55 $e_curlineref) = (0, 1, 2, 3, 4, 5, 6);
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*(?:.*)\):(.*)$/;
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/;
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" );
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;
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 */
122 # Contains all the file names, the first filename is the
123 # client. After that are all servers.
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
132 my $arrayref = shift @_;
133 my $pidhashref = shift @_;
134 my $stitchref = shift @_;
139 my $firstlineaftermarker = 0;
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;
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;
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])
164 #This second part of the comparision is a HACK
165 last LINE if ($pidprevious->[$e_backref] == 0);
166 $pidprevious = $pidprevious->[$e_backref];
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];
178 $lineref->[$e_backref] = $pidprevious;
179 $pidprevious->[$e_numchildren]++;
181 # lineref is sibling of pidprevious
182 $lineref->[$e_numchildren] = 0;
183 $lineref->[$e_backref] = $pidprevious->[$e_backref];
184 ($lineref->[$e_backref])->[$e_numchildren]++;
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];
193 $pidprevious->[$e_youngestchild] = $lineref;
194 $lineref->[$e_pidhead]=$pidprevious;
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;
209 match_rpcs($stitchref);
216 # Main loop, parses the debug log
220 my $input_files = shift;
222 my $stitch_ref = shift;
224 my $rpctrace = shift;
230 print "$pid, $rpctrace, $nodlm, $noclass, $nonet\n";
239 foreach $file (@$input_files) {
241 open(FILEHANDLE, $file) or die "Can't open file: $file\n";
242 while(<FILEHANDLE>) {
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})
251 next if ($trace && $parsed_line[$e_mask] !=
252 $tracemasks->{TRACE});
253 next if ($nodlm && hex($parsed_line[$e_subsys]) ==
255 next if ($noclass && hex($parsed_line[$e_subsys]) ==
256 $subsysnum->{CLASS});
257 next if ($nonet && (hex($parsed_line[$e_subsys]) ==
259 hex($parsed_line[$e_subsys]) ==
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}));
271 if (!exists($hasharray{$parsed_line[$e_pid]})) {
272 # Push a marker for the beginning of this PID
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 ];
295 push @$array_parsed, [ @parsed_line ];
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 ];
309 $array_parsed=create_links($array_parsed, \%hasharray, $stitch_ref);
310 #print_array($array_parsed);
311 return $array_parsed;
316 my $arrayref = shift;
317 foreach $lineref(@$arrayref){
318 if ($lineref->[$e_backref]==0){
319 print "MARKER LINE(addr): $lineref contents: [@$lineref]\n";
322 print "REGULAR LINE (addr) :$lineref contents:[@$lineref]\n";
328 sub print_rpcrelations {
330 my $rpchashref = shift;
331 foreach $rpckeys (sort keys %$rpchashref) {
332 $tmpref = $rpchashref->{$rpckeys};
333 #print "Key: $rpckeys, Contents: @$tmpref\n";
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"
364 my $rootline = shift;
366 my $tempref = $lineref->[$e_next];
371 if (($tempref->[$e_stack] > $rootline->[$e_stack]) ||
372 (($tempref->[$e_stack] <= $rootline->[$e_stack]) &&
373 ($tempref->[$e_function] == $rootline->[$e_function])
389 foreach $elem(@$inarg) {
390 next if ($elem =~ /$FILEOPTIONREGEX/);
391 $filearray[$idx] = $elem;
398 #$array_parsed=parse_file();
399 #print_array($array_parsed);