update to 1.8.4
[s390-tools.git] / scripts / zfcpdbf
blob3693d71774ef9f01029bd81076e7ff8df733bb5a
1 #!/usr/bin/perl
2 #==============================================================================
3 # Copyright IBM Corp. 2005, 2009.
5 # zfcpdbf
7 # Script to analyse trace data of ZFCP module logged in DBF.
9 # Author(s): Maxim Shchetynin <maxim@de.ibm.com>
10 # Teresa Gamez Zerban <gamezt@de.ibm.com>
12 # This file is part of s390-tools
14 # s390-tools is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; either version 2 of the License, or
17 # (at your option) any later version.
19 # s390-tools is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with s390-tools; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #==============================================================================
29 use strict;
30 use warnings;
31 use Getopt::Long;
32 use File::Basename;
34 my(%fsf_command) = (
35 '0x00000001' => 'send FCP command',
36 '0x00000002' => 'abort FCP command',
37 '0x00000005' => 'open port',
38 '0x00000006' => 'open unit',
39 '0x00000007' => 'close unit',
40 '0x00000008' => 'close port',
41 '0x00000009' => 'close port physical',
42 '0x0000000b' => 'send ELS',
43 '0x0000000c' => 'send generic',
44 '0x0000000d' => 'exchange config data',
45 '0x0000000e' => 'exchange port data',
46 '0x00000012' => 'download control file',
47 '0x00000013' => 'upload control file'
50 my(%protocol_status) = (
51 '0x00000001' => 'good',
52 '0x00000010' => 'QTCB version error',
53 '0x00000020' => 'sequence number error',
54 '0x00000040' => 'unsupported QTCB type',
55 '0x00000080' => 'host connection initializing',
56 '0x00000100' => 'FSF status presented',
57 '0x00000200' => 'duplicate request ID',
58 '0x00000400' => 'link down',
59 '0x00000800' => 'reestablished queue',
60 '0x01000000' => 'error state'
63 my(%fsf_status) = (
64 '0x00000000' => 'good',
65 '0x00000001' => 'port already open',
66 '0x00000002' => 'unit already open',
67 '0x00000003' => 'port handle not valid',
68 '0x00000004' => 'unit handle not valid',
69 '0x00000005' => 'handle mismatch',
70 '0x00000006' => 'service class not supported',
71 '0x00000009' => 'FCP LUN not valid',
72 '0x00000010' => 'access denied',
73 '0x00000011' => 'access type not valid',
74 '0x00000012' => 'LUN sharing violation',
75 '0x00000022' => 'command does not exist',
76 '0x00000030' => 'direction indicator not valid',
77 '0x00000033' => 'command length not valid',
78 '0x00000040' => 'max number of ports exceeded',
79 '0x00000041' => 'max number of units exceeded',
80 '0x00000050' => 'ELS rejected',
81 '0x00000051' => 'generic command rejected',
82 '0x00000052' => 'partially successful',
83 '0x00000053' => 'authorization failure',
84 '0x00000054' => 'ACT error detected',
85 '0x00000055' => 'control file update error',
86 '0x00000056' => 'control file too large',
87 '0x00000057' => 'access conflict detected',
88 '0x00000058' => 'conflicts overruled',
89 '0x00000059' => 'port boxed',
90 '0x0000005a' => 'unit boxed',
91 '0x0000005b' => 'exchange config/port data incomplete',
92 '0x00000060' => 'payload size mismatch',
93 '0x00000061' => 'request too large',
94 '0x00000062' => 'response too large',
95 '0x00000063' => 'SBAL mismatch',
96 '0x00000064' => 'open port without PRLI',
97 '0x000000ad' => 'adapter status available',
98 '0x000000af' => 'FCP RSP available',
99 '0x000000e2' => 'unknown command',
100 '0x000000e3' => 'unknown op subtype',
101 '0x000000e5' => 'invalid command option'
104 my(%unsolicited_status) = (
105 '0x00000001' => 'port closed',
106 '0x00000002' => 'incoming ELS',
107 '0x00000003' => 'sense data available',
108 '0x00000004' => 'bit error threshold',
109 '0x00000005' => 'link down',
110 '0x00000006' => 'link up',
111 '0x00000009' => 'notification lost',
112 '0x0000000a' => 'ACT updated',
113 '0x0000000b' => 'ACT hardened'
116 #COMMAND LINE OPTIONS
118 my $OPT_HBA = 0;
119 my $OPT_SAN = 0;
120 my $OPT_SCSI = 0;
121 my $OPT_REC = 0;
122 my $OPT_DEVICE = "";
123 my $OPT_PATH = "";
124 my $OPT_VERBOSE = 0;
125 my $OPT_HELP = 0;
126 my $OPT_VERSION = 0;
127 my $OPT_DATE = "on";
128 my $OPT_SINGLELINE =0;
130 #string for the singleline option
131 my $endl ="\n\t";
133 #PARSE COMMAND LINE OPTIONS
134 Getopt::Long::Configure('bundling');
136 GetOptions(
137 'd|device=s' => \$OPT_DEVICE,
138 'p|path=s' => \$OPT_PATH,
139 'V|verbose' => \$OPT_VERBOSE,
140 'h|help' => \$OPT_HELP,
141 'v|version' => \$OPT_VERSION,
142 'D|date=s' => \$OPT_DATE,
143 's|singleline' => \$OPT_SINGLELINE
144 ) or print_usage();
146 foreach(@ARGV) {
147 if($_ eq 'hba') {
148 $OPT_HBA = 1;
150 elsif( $_ eq 'scsi') {
151 $OPT_SCSI = 1;
153 elsif($_ eq 'rec') {
154 $OPT_REC = 1;
156 elsif($_ eq 'san') {
157 $OPT_SAN = 1;
161 #MAIN PROGRAM
163 if($OPT_HELP) {
164 print_usage();
167 if($OPT_VERSION) {
168 print_version();
171 if($OPT_SINGLELINE) {
172 $endl = "; ";
175 if($OPT_DATE ne "" && $OPT_DATE ne "on" and $OPT_DATE ne "off") {
176 print "Date has to be set 'on'(default) or 'off'" .
177 " and not '$OPT_DATE'.\n";
178 exit;
181 if($OPT_DEVICE eq "") {
182 $OPT_DEVICE = "*";
184 #Check if the adapter is set correct
185 else {
186 while(length($OPT_DEVICE) < 4) {
187 $OPT_DEVICE = '0' . $OPT_DEVICE;
190 if(length($OPT_DEVICE) == 4) {
191 $OPT_DEVICE = "0.0." . $OPT_DEVICE;
194 if(length($OPT_DEVICE) != 8 or $OPT_DEVICE !~ /^0\.0\./) {
195 printf "'$OPT_DEVICE' does not look like a FCP adapter.\n";
196 exit;
200 if($OPT_PATH eq "") {
201 $OPT_PATH = '/sys/kernel/debug/s390dbf';
204 #Check if path exist
205 if(! -d $OPT_PATH) {
206 printf $OPT_PATH . " does not exist. Is debugfs mounted?\n";
207 exit;
210 my @paths = glob($OPT_PATH . "/zfcp_" . $OPT_DEVICE . "_*");
211 unless(@paths) {
212 if($OPT_DEVICE ne "*") {
213 printf "No DBF files found for FCP adapter %s.\n",
214 $OPT_DEVICE;
216 else {
217 printf "No DBF files found at %s.\n",$OPT_PATH;
219 exit;
222 if($OPT_HBA) {
223 my @hba_paths = @{filter_paths("hba",@paths)};
224 trace_hba(@hba_paths);
226 elsif($OPT_SCSI) {
227 my @scsi_paths = @{filter_paths("scsi",@paths)};
228 trace_scsi(@scsi_paths);
230 elsif($OPT_REC) {
231 my @rec_paths = @{filter_paths("rec",@paths)};
232 trace_rec(@rec_paths);
234 else {
235 my @san_paths = @{filter_paths("san",@paths)};
236 trace_san(@san_paths);
239 #END MAIN PROGRAMM
242 #SUBS
245 # Filters the scsi trace for the specified ports
247 # \param Path to the traces
249 sub trace_scsi {
250 my(@scsi_paths) = @_;
252 foreach my $scsi_path (@scsi_paths) {
253 open(DBF,$scsi_path . "/structured") or
254 die("Cannot open DBF file " . $scsi_path . "/structured");
255 $scsi_path =~/(0\.0\.\w+)_\w+/;
256 my $adapter = $1;
257 my %scsi_hash;
258 while(<DBF>) {
259 if($_=~ /timestamp\s+([:\d]+)/) {
260 print_scsi($adapter,%scsi_hash);
261 %scsi_hash = ();
262 $scsi_hash{'timestamp'} = convert_time($1);
264 elsif ($_ =~ /(\w+)\s+(\w+)/) {
265 $scsi_hash{$1} = $2;;
268 print_scsi($adapter,%scsi_hash);
269 close(DBF);
274 # Print the scsi trace
276 # \param Adapter which is traced
277 # \param Data of the scsi trace
279 sub print_scsi {
280 my($adapter,%scsi_hash) = @_;
281 if(defined($scsi_hash{'timestamp'}) && defined($scsi_hash{'tag'}) &&
282 ($scsi_hash{'tag'} eq 'rslt' || $scsi_hash{'tag'} eq 'abrt' ||
283 $scsi_hash{'tag'} eq 'lrst' || $scsi_hash{'tag'} eq 'trst')) {
284 print "$adapter $scsi_hash{'timestamp'} " .
285 "SCSI serial=$scsi_hash{'scsi_serial'}" . $endl .
286 "id=$scsi_hash{'scsi_id'} lun=$scsi_hash{'scsi_lun'} " .
287 "command=$scsi_hash{'scsi_cmnd'}" . $endl;
288 if($OPT_VERBOSE && $scsi_hash{'scsi_retries'} ne hex 0) {
289 print "retry $scsi_hash{'scsi_retries'} ".
290 "from $scsi_hash{'scsi_allowed'}";
293 if($scsi_hash{'fcp_rsp_validity'} ne '0x00') {
294 print "FCP_RSP validity=" .
295 "$scsi_hash{'fcp_rsp_validity'} ";
296 if($OPT_VERBOSE) {
297 print "scsi_status=" .
298 "$scsi_hash{'fcp_rsp_scsi_status'} " .
299 $endl . "residual=" .
300 "$scsi_hash{'fcp_rsp_resid'} " .
301 "code=0x$scsi_hash{'fcp_rsp_code'} ";
305 if($OPT_VERBOSE && $scsi_hash{'fcp_sns_info_len'} ne hex 0) {
306 print "FCP_SNS info=$scsi_hash{'fcp_sns_info'}";
308 print "\n";
313 # Filters the hba trace for the specified ports
315 # \param Path to the traces
317 sub trace_hba {
318 my(@hba_paths) = @_;
320 foreach my $hba_path (@hba_paths) {
321 open(DBF,$hba_path . "/structured") or
322 die("Cannot open DBF file " . $hba_path . "/structured");
323 $hba_path =~/(0\.0\.\w+)_\w+/;
324 my $adapter = $1;
325 my %hba_hash;
326 while(<DBF>) {
327 if($_ =~ /timestamp\s+([:\d]+)/) {
328 print_hba($adapter, %hba_hash);
329 %hba_hash = ();
330 $hba_hash{'timestamp'} = convert_time($1);
332 elsif ($_ =~ /^(\w+)\s+(\w+)$/) {
333 $hba_hash{$1} = $2;
335 elsif ($_ =~ /^(\w+)\s+(\w+\s\w+\s\w+\s\w+)$/) {
336 $hba_hash{$1} = $2;
338 elsif ($_ =~ /^(\w+)\s+(\w+\s\w+)$/) {
339 $hba_hash{$1} = $2;
342 print_hba($adapter,%hba_hash);
343 close DBF;
348 # Print the hba trace
350 # \param Adapter which is traced
351 # \param Data of the hba trace
353 sub print_hba {
354 my($adapter,%hba_hash) = @_;
355 if(defined($hba_hash{'timestamp'})) {
356 #Unsolicited status
357 if(defined($hba_hash{'tag'}) && $hba_hash{'tag'} eq "stat") {
358 if(defined($hba_hash{'tag2'}) &&
359 $hba_hash{'tag2'} eq "read") {
360 if(defined($hba_hash{'timestamp'}) &&
361 defined($hba_hash{'status_type'}) &&
362 defined($hba_hash{'status_subtype'}) &&
363 defined($hba_hash{'queue_designator'})) {
364 print "$adapter $hba_hash{
365 'timestamp'} " .
366 "unsolicited status" .
367 " '$unsolicited_status{
368 $hba_hash{'status_type'}}'"
369 . $endl .
370 "subtype= $hba_hash{'status_subtype'}"
371 . $endl .
372 "queue designator= $hba_hash{
373 'queue_designator'}" . $endl ;
376 elsif(defined($hba_hash{'tag2'}) &&
377 $hba_hash{'tag2'} eq "dism") {
378 print "status read request dissmissed";
380 elsif(defined($hba_hash{'tag2'}) &&
381 $hba_hash{'tag2'} eq "fail") {
382 print "status read request failed";
385 elsif(defined($hba_hash{'tag'}) && $hba_hash{'tag'} eq "resp") {
386 if(defined($hba_hash{'tag2'}) &&
387 ($hba_hash{'tag2'} eq "perr" ||
388 $hba_hash{'tag2'} eq "ferr" ||
389 $hba_hash{'tag2'} eq "open" ||
390 $hba_hash{'tag2'} eq "qtcb")) {
391 print "$adapter $hba_hash{'timestamp'} " .
392 "response to '$fsf_command{
393 $hba_hash{'fsf_command'}}'" . $endl;
394 print "protocol status='$protocol_status{
395 $hba_hash{'fsf_prot_status'}}'" .
396 "FSF status='$fsf_status{
397 $hba_hash{'fsf_status'}}'" . $endl ;
400 else {
401 next;
404 if($OPT_VERBOSE) {
405 print "protocol status qualifier=" .
406 "'$hba_hash{'fsf_prot_status_qual'}'" . $endl .
407 "FSF status qualifier=" .
408 "'$hba_hash{'fsf_status_qual'}'" . $endl .
409 "FSF request status " .
410 "'$hba_hash{'fsf_req_status'}'" . $endl .
411 "SBAL=$hba_hash{'sbal_first'}/$hba_hash{
412 'sbal_last'}/$hba_hash{'sbal_response'} " .
413 "(fist/last/response)" . $endl;
416 if($hba_hash{'fsf_command'} eq '0x00000002') {
417 print "Abort FSF request " .
418 "ID=$hba_hash{'abort_fsf_reqid'} " .
419 "seqno=$hba_hash{'abort_fsf_seqno'}";
421 elsif($hba_hash{'fsf_command'} eq '0x00000005' ||
422 $hba_hash{'fsf_command'} eq '0x00000008' ||
423 $hba_hash{'fsf_command'} eq '0x00000009') {
424 print "WWPN=$hba_hash{'wwpn'} D_ID=$hba_hash{'d_id'} ";
425 print "port handle=$hba_hash{'port_handle'}";
427 elsif($hba_hash{'fsf_command'} eq '0x00000006' ||
428 $hba_hash{'fsf_command'} eq '0x00000007') {
429 print "WWPN=$hba_hash{'wwpn'} " .
430 "LUN=$hba_hash{'fcp_lun'}" . $endl;
431 print "port handle=$hba_hash{'port_handle'}" .
432 " LUN handle=$hba_hash{'lun_handle'}";
434 elsif($hba_hash{'fsf_command'} eq '0x0000000b' ) {
435 print "D_ID=$hba_hash{'d_id'} LS " .
436 "code=$hba_hash{'port_handle'}";
438 print"\n";
443 # Filters the rec trace for the specified ports
445 # \param Path to the traces
447 sub trace_rec {
448 my(@rec_paths) = @_;
450 foreach my $rec_path ( @rec_paths ) {
451 open(DBF,$rec_path . "/structured") or
452 die("Cannot open DBF file " . $rec_path . "/structured");
453 $rec_path =~/(0\.0\.\w+)_\w+/;
454 my $adapter = $1;
455 my %rec_hash;
456 while(<DBF>) {
457 if($_ =~ /timestamp\s+([:\d]+)/) {
458 print_rec($adapter,%rec_hash);
459 %rec_hash = ();
460 $rec_hash{'timestamp'} = convert_time($1);
461 print "\n";
463 elsif ($_ =~ /(\w+)\s+(\w+)/) {
464 $rec_hash{$1} = $2;
467 print_rec($adapter,%rec_hash);
468 print "\n";
469 close DBF;
474 # Prints the rec traces
476 # \param Adapter which is traced
477 # \param Data of the rec trace
479 sub print_rec {
480 my($adapter,%rec_hash) = @_;
481 if(defined($rec_hash{'timestamp'}) && defined($rec_hash{'tag'}) &&
482 defined($rec_hash{'hint'}) && defined($rec_hash{'wwpn'}) &&
483 defined($rec_hash{'fcp_lun'})) {
484 print "$adapter $rec_hash{'timestamp'}\ttag=$rec_hash{'tag'} " .
485 "hint=$rec_hash{'hint'}" . $endl .
486 "WWPN='$rec_hash{'wwpn'}' lun='$rec_hash{'fcp_lun'}'";
491 # Filters the san trace for the specified ports
493 # \param Path to the traces
495 sub trace_san {
496 my(@san_paths) = @_;
498 foreach my $san_path (@san_paths) {
499 open(DBF,$san_path . "/structured") or
500 die("Cannnot open DBF file " . $san_path . "/structured");
501 $san_path =~ /(0\.0\.\w+)_\w+/;
502 my $adapter = $1;
503 my %san_hash;
504 while(<DBF>) {
505 if($_ =~ /timestamp\s+([:\d]+)/) {
506 #Every new block starts with a timestamp and
507 #since we cannot be sure that a block ends with
508 #a blank line we check for the next timestamp
509 print_san($adapter,%san_hash);
510 %san_hash = ();
511 $san_hash{'timestamp'} = convert_time($1);
513 elsif ($_ =~ /(\w+)\s+(\w+)/) {
514 $san_hash{$1} = $2;
517 print_san($adapter,%san_hash);
518 print"\n";
519 close DBF;
521 return;
525 # Prints the san traces
527 # \param Adapter which is traced
528 # \param Data of the san trace
530 sub print_san {
531 my($adapter,%san_hash) = @_;
533 if(defined($san_hash{'timestamp'}) && defined($san_hash{'s_id'}) &&
534 defined($san_hash{'d_id'}) && defined($san_hash{'tag'})) {
535 my $tag;
536 if($san_hash{'tag'} eq "octc") {
537 $tag = "CT request";
539 elsif($san_hash{'tag'} eq "rctc") {
540 $tag = "CT response";
542 elsif($san_hash{'tag'} eq "oels") {
543 $tag = "ELS request";
545 elsif($san_hash{'tag'} eq "rels") {
546 $tag = "ELS respsone";
548 elsif($san_hash{'tag'} eq "iels") {
549 $tag = "ELS incoming";
551 print "$adapter $san_hash{'timestamp'}\t$san_hash{'s_id'} " .
552 " -> $san_hash{'d_id'} $tag \n";
557 # Converts the Unix time to localtime an returns it
558 # depending on the --date option.
560 # \param UNIX Timestamp to convert
561 # \return Formated Localtime
563 sub convert_time {
564 my($timestamp) = @_;
566 my @conv_time = split(/:/,$timestamp);
567 my @local_t = localtime($conv_time[0]);
568 if($OPT_DATE eq "on") {
569 return (sprintf "%04d-%02d-%02d %02d:%02d:%02d.%d",
570 ($local_t[5]+1900) ,$local_t[4],$local_t[3],$local_t[2],
571 $local_t[1],$local_t[0], $conv_time[1]);
573 else {
574 return (sprintf "%02d:%02d:%02d.%d",$local_t[2],
575 $local_t[1],$local_t[0], $conv_time[1]);
580 # Filters the path to trace
582 #\param The filter for the paths
583 #\param Path to the traces
584 #\return The filtered pathes
586 sub filter_paths {
587 my($filter,@paths) = @_;
589 my @filtered_paths;
591 foreach my $path (@paths) {
592 if($path =~ /_$filter$/) {
593 push(@filtered_paths,$path);
596 unless(@filtered_paths) {
597 print "No traces for '$filter' found\n";
598 exit;
600 return \@filtered_paths;
604 # Prints the version
606 sub print_version {
608 my ($s390tools_version) = '%S390_TOOLS_VERSION%';
609 printf "zfcpdbf: version %s\n", $s390tools_version;
610 printf "Copyright IBM Corp. 2005,2009\n";
611 exit;
615 # Prints the usage
617 sub print_usage {
618 print
620 Usage: zfcpdbf [COMMAND] [OPTIONS]
622 Sort debug data of all active or specified FCP adapter(s).
623 -h, --help display this help and exit
624 -v, --version display version information and exit
625 commands:
626 hba trace all FSF requests
627 san trace SAN activity (default)
628 scsi trace SCSI- and corresponding FCP-commands
629 rec trace REC records
630 options:
631 -d, --device=<device> FCP adapter device number
632 -p, --path=<path> specifiy path to DBF files
633 -D, --dates={off|on} show date as well as time
634 -s, --singleline output in one line per event
635 -V, --verbose verbose output
638 exit;